diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/FileUtils.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/FileUtils.java new file mode 100644 index 000000000..d15740614 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/FileUtils.java @@ -0,0 +1,65 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.database; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class FileUtils { + + public static void createDirectory(final String path) throws IOException { + Files.createDirectory(Paths.get(path)); + } + + public static boolean createDirectory(final String path, final String secondPath) { + return Paths.get(path, secondPath).normalize().toFile().mkdir(); + } + + public static boolean delete(final String path, final String secondPath) { + return Paths.get(path, secondPath).normalize().toFile().delete(); + } + + public static boolean exists(final String path) { + return Paths.get(path).normalize().toFile().exists(); + } + + public static boolean exists(final String path, final String secondPath) { + return Paths.get(path, secondPath).normalize().toFile().exists(); + } + + public static Path get(final String path) { + return Paths.get(path).toAbsolutePath().normalize(); + } + + public static Path get(final String path, final String secondPath) { + return Paths.get(path, secondPath).toAbsolutePath().normalize(); + } + + public static String getFileName(final String path) { + return Paths.get(path).toAbsolutePath().normalize().getFileName().toString(); + } + + public static String getFileName(final String path, final String secondPath) { + return Paths.get(path, secondPath).normalize().getFileName().toString(); + } + + public static String getParentName(final String path) { + return Paths.get(path).normalize().getParent().getFileName().toString(); + } + + public static String getParentName(final String path, final String secondPath) { + return Paths.get(path, secondPath).normalize().getParent().getFileName().toString(); + } + + public static boolean isDirectory(final String path) { + return Paths.get(path).toAbsolutePath().normalize().toFile().isDirectory(); + } + + public static boolean isDirectory(final String path, final String secondPath) { + return Paths.get(path, secondPath).toAbsolutePath().normalize().toFile().isDirectory(); + } + + public static String toAbsolutePath(final String path) { + return Paths.get(path).toAbsolutePath().normalize().toString(); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/JSONUtils.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/JSONUtils.java new file mode 100644 index 000000000..89d4a5682 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/JSONUtils.java @@ -0,0 +1,135 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.database; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Storeable; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +import java.util.List; +import java.util.NoSuchElementException; + +public class JSONUtils { + + public static String storeableToString(Table table, Storeable storeable) { + if (storeable == null) { + return null; + } + StringBuilder builder = new StringBuilder("["); + for (int i = 0; i < table.getColumnsCount(); ++i) { + builder.append(JSONUtils.valueInStoreable(table, storeable, i)).append(", "); + } + builder.deleteCharAt(builder.length() - 1); + builder.deleteCharAt(builder.length() - 1); + builder.append("]"); + return builder.toString(); + } + + public static String valueInStoreable(Table table, Storeable storeable, int index) { + Class type = table.getColumnType(index); + if (storeable == null) { + return "null"; + } + if (type == Integer.class) { + return storeable.getIntAt(index).toString(); + } + if (type == Long.class) { + return storeable.getLongAt(index).toString(); + } + if (type == Byte.class) { + return storeable.getByteAt(index).toString(); + } + if (type == Float.class) { + return storeable.getFloatAt(index).toString(); + } + if (type == Double.class) { + return storeable.getDoubleAt(index).toString(); + } + if (type == Boolean.class) { + return storeable.getBooleanAt(index).toString(); + } + if (type == String.class) { + return storeable.getStringAt(index).toString(); + } + return "null"; + } + + public static Class stringToType(String string) { + if (string.equals("int")) { + return Integer.class; + } + if (string.equals("long")) { + return Long.class; + } + if (string.equals("byte")) { + return Byte.class; + } + if (string.equals("float")) { + return Float.class; + } + if (string.equals("double")) { + return Double.class; + } + if (string.equals("bool")) { + return Boolean.class; + } + if (string.equals("string")) { + return String.class; + } + throw new NoSuchElementException(); + } + + public static Storeable stringToStoreable(List> types, String string) { + if (string == null) { + return null; + } + return new StoreableByVolodden(string, types); + } + + public static Object stringToOneElementOfStoreable(Class type, String string) { + if (type == Integer.class) { + return Integer.parseInt(string); + } + if (type == Long.class) { + return Long.parseLong(string); + } + if (type == Byte.class) { + return Byte.parseByte(string); + } + if (type == Float.class) { + return Float.parseFloat(string); + } + if (type == Double.class) { + return Double.parseDouble(string); + } + if (type == Boolean.class) { + return Boolean.parseBoolean(string); + } + if (type == String.class) { + return string; + } + return null; + } + + public static String typeToString(Class type) { + if (type == Integer.class) { + return "int"; + } + if (type == Long.class) { + return "long"; + } + if (type == Byte.class) { + return "byte"; + } + if (type == Float.class) { + return "float"; + } + if (type == Double.class) { + return "double"; + } + if (type == Boolean.class) { + return "bool"; + } + if (type == String.class) { + return "string"; + } + throw new NoSuchElementException(); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/StoreableByVolodden.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/StoreableByVolodden.java new file mode 100644 index 000000000..199c6aeff --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/StoreableByVolodden.java @@ -0,0 +1,207 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.database; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.ColumnFormatException; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Storeable; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +import java.util.*; + +public class StoreableByVolodden implements Storeable{ + + private List> types; + private List values; + private Set> validTypes; + + public StoreableByVolodden(Table table) { + initializeValidTypes(); + List> newTypes = new ArrayList<>(); + for (int i = 0; i < table.getColumnsCount(); ++i) { + newTypes.add(table.getColumnType(i)); + } + for (Class type : newTypes) { + if (!validTypes.contains(type)) { + throw new ColumnFormatException("Invalid column type"); + } + } + types = newTypes; + values = new ArrayList<>(); + for (int i = 0; i < types.size(); i++) { + values.add(null); + } + } + + public StoreableByVolodden(Table table, List newValues) { + this(table); + if (newValues.size() != values.size()) { + throw new IndexOutOfBoundsException("Other size of values"); + } else { + for (int i = 0; i < newValues.size(); ++i) { + if (newValues.get(i).getClass() != types.get(i)) { + throw new ColumnFormatException("Other type"); + } else { + values.set(i, newValues.get(i)); + } + } + } + } + + public StoreableByVolodden(Table table, String[] array) { + this(table); + if (array.length != values.size()) { + throw new IndexOutOfBoundsException("Other size of values"); + } else { + for (int i = 0; i < array.length; ++i) { + if (array[i].equals("null")) { + values.set(i, null); + } + if (table.getColumnType(i) == Integer.class) { + values.set(i, Integer.parseInt(array[i])); + } + if (table.getColumnType(i) == Long.class) { + values.set(i, Long.parseLong(array[i])); + } + if (table.getColumnType(i) == Byte.class) { + values.set(i, Byte.parseByte(array[i])); + } + if (table.getColumnType(i) == Float.class) { + values.set(i, Float.parseFloat(array[i])); + } + if (table.getColumnType(i) == Double.class) { + values.set(i, Double.parseDouble(array[i])); + } + if (table.getColumnType(i) == Boolean.class) { + values.set(i, Boolean.parseBoolean(array[i])); + } + if (table.getColumnType(i) == String.class) { + values.set(i, array[i]); + } + } + } + } + + public StoreableByVolodden(String string, List> inputTypes) { + types = inputTypes; + values = new ArrayList(); + if (string == null) { + string = "[null]"; + } + string = string.substring(1, string.length() - 1); + String[] array = string.split(", "); + for (int i = 0; i < array.length; ++i) { + if (array[i].equals("null")) { + values.add(null); + continue; + } + if (types.get(i) == Integer.class) { + values.add(Integer.parseInt(array[i])); + } + if (types.get(i) == Long.class) { + values.add(Long.parseLong(array[i])); + } + if (types.get(i) == Byte.class) { + values.add(Byte.parseByte(array[i])); + } + if (types.get(i) == Float.class) { + values.add(Float.parseFloat(array[i])); + } + if (types.get(i) == Double.class) { + values.add(Double.parseDouble(array[i])); + } + if (types.get(i) == Boolean.class) { + values.add(Boolean.parseBoolean(array[i])); + } + if (types.get(i) == String.class) { + values.add(array[i]); + } + } + } + + private void initializeValidTypes() { + if (validTypes == null) { + validTypes = new HashSet<>(); + validTypes.add(Integer.class); + validTypes.add(Long.class); + validTypes.add(Byte.class); + validTypes.add(Double.class); + validTypes.add(Float.class); + validTypes.add(Boolean.class); + validTypes.add(String.class); + } + } + + @Override + public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + if (value != null) { + checkFormat(columnIndex, value.getClass()); + } + values.set(columnIndex, value); + } + + @Override + public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { + checkBounds(columnIndex); + return values.get(columnIndex); + } + + @Override + public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkFormat(columnIndex, Integer.class); + return (Integer) values.get(columnIndex); + } + + @Override + public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkFormat(columnIndex, Long.class); + return (Long) values.get(columnIndex); + } + + @Override + public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkFormat(columnIndex, Byte.class); + return (Byte) values.get(columnIndex); + } + + @Override + public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkFormat(columnIndex, Float.class); + return (Float) values.get(columnIndex); + } + + @Override + public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkFormat(columnIndex, Double.class); + return (Double) values.get(columnIndex); + } + + @Override + public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkFormat(columnIndex, Boolean.class); + return (Boolean) values.get(columnIndex); + } + + @Override + public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkFormat(columnIndex, String.class); + return (String) values.get(columnIndex); + } + + private void checkBounds(int index) throws IndexOutOfBoundsException { + if (index >= types.size()) { + throw new IndexOutOfBoundsException("Invalid index."); + } + } + + private void checkFormat(int index, Class type) throws ColumnFormatException { + if (!types.get(index).equals(type)) { + throw new ColumnFormatException("Invalid format."); + } + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableByVolodden.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableByVolodden.java new file mode 100644 index 000000000..d3e591232 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableByVolodden.java @@ -0,0 +1,489 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.database; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions.DatabaseCorruptedException; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.ErrorFunctions; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.ColumnFormatException; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Storeable; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class TableByVolodden implements Table { + + private String dbPath; + private FileMap fileMap; + private List> types; + + private ReadWriteLock rwlLock = new ReentrantReadWriteLock(); + + private final String signatureFileName = "signature.tsv"; + + public TableByVolodden(final String path) throws Exception { + dbPath = FileUtils.toAbsolutePath(path); + fileMap = new FileMap(dbPath); + } + + //--- + class HelpMap { + public HashMap map; + public HelpMap() { + map = new HashMap(); + } + } + + class FileMap implements Map, AutoCloseable { + + private final int folders = 16; + private final int files = 16; + private final String encoding = "UTF-8"; + private final String dir = ".dir"; + private final String dat = ".dat"; + + private String databasePath; + private Map database; + private ThreadLocal> diff; + + public FileMap(final String dbPath) throws Exception { + databasePath = dbPath; + database = new HashMap(); + diff = ThreadLocal.withInitial(() -> new HashMap()); + if (FileUtils.exists(databasePath)) { + + rwlLock.readLock().lock(); + try { + readFromDisk(); + readTypes(); + } finally { + rwlLock.readLock().unlock(); + } + } else { + if (types == null) { + throw new Exception("Table was not found"); + } else { + FileUtils.createDirectory(databasePath); + File file = new File(FileUtils.get(databasePath, signatureFileName).toString()); + file.createNewFile(); + try (PrintWriter out = new PrintWriter(file)) { + int i = 0; + for (Class type : types) { + if (i++ != 0) { + out.print(" "); + } + out.print(JSONUtils.typeToString(type)); + } + } catch (Exception e) { + throw e; + } + } + } + } + + private void readTypes() throws DatabaseCorruptedException { + types = new ArrayList<>(); + List list; + try (Scanner in = new Scanner(new File(FileUtils.get(dbPath, signatureFileName).toString()))) { + list = Arrays.asList(in.nextLine().split("\\s+")); + for (String type : list) { + types.add(JSONUtils.stringToType(type)); + } + } catch (FileNotFoundException e) { + throw new DatabaseCorruptedException("Can't find a file with signatures."); + } catch (IllegalArgumentException e) { + throw new DatabaseCorruptedException(signatureFileName + " is corrupted."); + } catch (NoSuchElementException e) { + throw new DatabaseCorruptedException(signatureFileName + " is corrupted."); + } + } + + private void readFromDisk() throws Exception { + for (int i = 0; i < folders; ++i) { + for (int j = 0; j < files; ++j) { + Path somePath = Paths.get(databasePath, Integer.toString(i) + dir, Integer.toString(j) + + dat).normalize(); + if (somePath.toFile().exists()) { + try (DataInputStream input = new DataInputStream(new FileInputStream(somePath.toString()))) { + while (true) { + try { + String key = readOneWordFromDisk(input); + String value = readOneWordFromDisk(input); + + if ((Math.abs(key.hashCode()) % folders != i) + || (Math.abs(key.hashCode()) / folders % files != j)) { + throw new Exception("wrong input"); + } + database.put(key, value); + } catch (EOFException e) { + break; + } + } + } catch (Exception e) { + ErrorFunctions.errorRead(); + } + } + } + } + } + + private String readOneWordFromDisk(DataInputStream input) throws Exception { + byte[] word = new byte[input.readInt()]; + input.readFully(word); + return new String(word, encoding); + } + + private void writeOnDisk() throws Exception { + HelpMap[][] helpMap = new HelpMap[folders][files]; + for (int i = 0; i < folders; ++i) { + for (int j = 0; j < files; ++j) { + helpMap[i][j] = new HelpMap(); + } + } + Set keys = database.keySet(); + for (String key : keys) { + helpMap[Math.abs(key.hashCode()) % folders][Math.abs(key.hashCode()) / folders % files].map + .put(key, database.get(key)); + } + for (int i = 0; i < folders; ++i) { + for (int j = 0; j < files; ++j) { + Path somePath = Paths.get(databasePath, Integer.toString(i) + dir, Integer.toString(j) + + dat).normalize(); + if (somePath.toFile().exists()) { + if (!somePath.toFile().delete()) { + ErrorFunctions.smthWrong("write", "file is not deleted"); + } + } + } + Path somePath = Paths.get(databasePath, Integer.toString(i) + dir).normalize(); + if (somePath.toFile().exists()) { + if (!somePath.toFile().delete()) { + ErrorFunctions.smthWrong("write", "folder is not deleted"); + } + } + } + for (int i = 0; i < folders; ++i) { + for (int j = 0; j < files; ++j) { + Set keyList = helpMap[i][j].map.keySet(); + if (!keyList.isEmpty()) { + Path somePath = Paths.get(databasePath, Integer.toString(i) + dir).normalize(); + if (!somePath.toFile().exists()) { + Files.createDirectory(somePath); + } + somePath = Paths.get(somePath.toString(), j + dat).normalize(); + Files.createFile(somePath); + + try (FileOutputStream output = new FileOutputStream(somePath.toString())) { + for (String key : keyList) { + writeOneWordOnDisk(key, output); + writeOneWordOnDisk(database.get(key), output); + } + } catch (Exception e) { + ErrorFunctions.errorWrite(); + } + } + } + } + } + + private void writeOneWordOnDisk(final String word, FileOutputStream output) throws Exception { + ByteBuffer buffer = ByteBuffer.allocate(4); + byte[] wordByte = buffer.putInt(word.getBytes(encoding).length).array(); + output.write(wordByte); + output.write(word.getBytes(encoding)); + } + + private void upgrade() throws Exception { + for (String key : diff.get().keySet()) { + if (diff.get().get(key) == null) { + database.remove(key); + } else { + database.put(key, diff.get().get(key)); + } + } + diff.get().clear(); + writeOnDisk(); + } + + private void downgrade() { + diff.get().clear(); + } + + public int numUncommitedChanges() { + return diff.get().size(); + } + + @Override + public void close() throws Exception { + } + + //Not used. + @Override + public void clear() { + database.clear(); + diff.get().clear(); + } + + //Not used. + @Override + public boolean containsKey(Object key) { + if (database.containsKey(key)) { + if (diff.get().containsKey(key)) { + return (diff.get().get(key) != null); + } + return true; + } else { + return diff.get().containsKey(key); + } + } + + //Not used. + @Override + public boolean containsValue(Object value) { + if (diff.get().containsValue(value)) { + return true; + } else { + for (String key : database.keySet()) { + if (database.get(key).equals(value)) { + if (!diff.get().containsKey(key)) { + return true; + } + } + } + return false; + } + } + + // Not used. + @Override + public Set> entrySet() { + return null; + } + + @Override + public String get(Object key) { + if (diff.get().containsKey(key)) { + return diff.get().get(key); + } else { + return database.get(key); + } + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public Set keySet() { + + Set keysList = new HashSet(); + if (isEmpty()) { + return keysList; + } + + for (String key : database.keySet()) { + if (diff.get().containsKey(key)) { + if (diff.get().get(key) != null) { + keysList.add(key); + } + } else { + keysList.add(key); + } + } + for (String key : diff.get().keySet()) { + if (!keysList.contains(key)) { + keysList.add(key); + } + } + return keysList; + } + + @Override + public String put(String key, String value) { + if (diff.get().containsKey(key)) { + if (database.containsKey(key)) { + if (database.get(key).equals(value)) { + String oldValue = diff.get().get(key); + diff.get().remove(key); + return oldValue; + } else { + return diff.get().put(key, value); + } + } else { + return diff.get().put(key, value); + } + } else { + if (database.containsKey(key)) { + if (database.get(key).equals(value)) { + return database.get(key); + } else { + diff.get().put(key, value); + return database.get(key); + } + } else { + return diff.get().put(key, value); + } + } + } + + // Not used. + @Override + public void putAll(Map m) { + return; + } + + @Override + public String remove(Object key) { + if (diff.get().containsKey(key)) { + if (diff.get().get(key) == null) { + return null; + } else { + return diff.get().put((String) key, null); + } + } else { + if (database.containsKey(key)) { + diff.get().put((String) key, null); + } + return database.get(key); + } + } + + @Override + public int size() { + int result = database.size(); + for (String key : database.keySet()) { + if (diff.get().containsKey(key)) { + if (diff.get().get(key) == null) { + --result; + } + } + } + for (String key : diff.get().keySet()) { + if (!database.containsKey(key)) { + if (diff.get().get(key) != null) { + ++result; + } + } + } + return result; + } + + // Not used. + @Override + public Collection values() { + return null; + } + } + + //--- + + @Override + public Storeable put(String key, Storeable value) throws ColumnFormatException { + + rwlLock.readLock().lock(); + try { + return JSONUtils.stringToStoreable(types, fileMap.put(key, JSONUtils.storeableToString(this, value))); + } finally { + rwlLock.readLock().unlock(); + } + } + + @Override + public Storeable remove(String key) { + rwlLock.readLock().lock(); + try { + return JSONUtils.stringToStoreable(types, fileMap.remove(key)); + } finally { + rwlLock.readLock().unlock(); + } + } + + @Override + public int size() { + rwlLock.readLock().lock(); + try { + return fileMap.size(); + } finally { + rwlLock.readLock().unlock(); + } + } + + @Override + public List list() { + rwlLock.readLock().lock(); + try { + List keys = new ArrayList(); + for (String key : fileMap.keySet()) { + keys.add(key); + } + return keys; + } finally { + rwlLock.readLock().unlock(); + } + } + + @Override + public int commit() { + + rwlLock.writeLock().lock(); + try { + int changes = 0; + try { + fileMap.upgrade(); + } catch (Exception exception) { + exception.printStackTrace(); + } + return changes; + } finally { + rwlLock.writeLock().unlock(); + } + } + + @Override + public int rollback() { + int changes = fileMap.numUncommitedChanges(); + fileMap.downgrade(); + return changes; + } + + @Override + public int getNumberOfUncommittedChanges() { + rwlLock.readLock().lock(); + try { + return fileMap.numUncommitedChanges(); + } finally { + rwlLock.readLock().unlock(); + } + } + + @Override + public int getColumnsCount() { + return types.size(); + } + + @Override + public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { + return types.get(columnIndex); + } + + @Override + public String getName() { + return FileUtils.getFileName(dbPath); + } + + @Override + public Storeable get(String key) { + rwlLock.readLock().lock(); + try { + return JSONUtils.stringToStoreable(types, fileMap.get(key)); + } finally { + rwlLock.readLock().unlock(); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableProviderByVolodden.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableProviderByVolodden.java new file mode 100644 index 000000000..f949a7999 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableProviderByVolodden.java @@ -0,0 +1,241 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.database; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.ErrorFunctions; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.ColumnFormatException; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Storeable; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.TableProvider; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class TableProviderByVolodden implements TableProvider { + + private String dbPath; + private Set tables; + + + private ReadWriteLock rwlLock = new ReentrantReadWriteLock(); + + private final String signatureFileName = "signature.tsv"; + + + private final String regExpToSplitJSON = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"; + + public TableProviderByVolodden(String dir) throws Exception { + dbPath = FileUtils.toAbsolutePath(dir); + if (!FileUtils.exists(dbPath)) { + FileUtils.createDirectory(dbPath); + } + tables = readOldTables(Paths.get(dbPath)); + } + + @Override + public Table getTable(String name) throws IllegalArgumentException { + + rwlLock.readLock().lock(); + try { + if (!FileUtils.exists(dbPath, name)) { + ErrorFunctions.notExists("get table", name); + } + Table dbTable; + try { + dbTable = new TableByVolodden(FileUtils.get(dbPath, name).toString()); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + return dbTable; + } finally { + rwlLock.readLock().unlock(); + } + } + + @Override + public Table createTable(String name, List> columnTypes) throws IOException { + if (name == null) { + ErrorFunctions.nameIsNull("create", name); + } + + rwlLock.writeLock().lock(); + try { + Table dbTable = null; + if (FileUtils.exists(dbPath, name)) { + if (!FileUtils.isDirectory(dbPath, name)) { + ErrorFunctions.tableNameIsFile("create", name); + } + } else { + boolean exists = false; + try { + exists = FileUtils.createDirectory(dbPath, name); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + if (exists) { + try { + File file = Paths.get(dbPath, name, signatureFileName).toFile(); + file.createNewFile(); + try (PrintWriter out = new PrintWriter(file)) { + int i = 0; + for (Class type : columnTypes) { + if (i++ != 0) { + out.print(" "); + } + out.print(JSONUtils.typeToString(type)); + } + } catch (Exception e) { + throw e; + } + dbTable = new TableByVolodden(Paths.get(dbPath, name).normalize().toFile().toString()); + tables.add(name); + } catch (Exception exception) { + exception.printStackTrace(); + } + } else { + ErrorFunctions.notMkdir("create", name); + } + } + return dbTable; + } finally { + rwlLock.writeLock().unlock(); + } + } + + @Override + public void removeTable(String name) throws IOException { + if (!FileUtils.exists(dbPath, name)) { + throw new IllegalStateException("[" + name + "] not exists"); + } + if (!FileUtils.getParentName(dbPath, name).equals(FileUtils.getFileName(dbPath))) { + ErrorFunctions.notExists("drop", name); + } + if (!FileUtils.isDirectory(dbPath)) { + ErrorFunctions.notDirectory("drop", name); + } + + rwlLock.writeLock().lock(); + + try { + recursiveDrop(FileUtils.get(dbPath)); + tables.remove(name); + } catch (Exception exception) { + throw new RuntimeException(exception); + } finally { + rwlLock.writeLock().unlock(); + } + } + + @Override + public Storeable deserialize(Table table, String value) throws ParseException { + if (!value.startsWith("[")) { + throw new ParseException("Can't deserialize <" + value + ">: argument doesn't start with \"[\".", 0); + } + if (!value.endsWith("]")) { + throw new ParseException("Can't deserialize <" + value + ">: argument doesn't end with \"]\".", + value.length() - 1); + } + value = value.substring(1, value.length() - 1); + String[] parsedValues = value.split(regExpToSplitJSON); + Storeable answer = createFor(table); + int i = 0; + try { + for (i = 0; i < parsedValues.length; ++i) { + answer.setColumnAt(i, JSONUtils.stringToOneElementOfStoreable(table.getColumnType(i), parsedValues[i])); + } + } catch (ColumnFormatException e) { + throw new ParseException(e.getMessage(), i); + } catch (IndexOutOfBoundsException e) { + throw new ParseException(e.getMessage(), i); + } + if (i < table.getColumnsCount()) { + throw new ParseException("Too few elements.", i); + } + return answer; + } + + @Override + public String serialize(Table table, Storeable value) throws ColumnFormatException { + return JSONUtils.storeableToString(table, value); + } + + @Override + public Storeable createFor(Table table) { + return new StoreableByVolodden(table); + } + + @Override + public Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException { + return new StoreableByVolodden(table, values); + } + + @Override + public List getTableNames() { + List result = new ArrayList<>(); + for (String name : tables) { + result.add(name); + } + return result; + } + + private static void recursiveDrop(final Path pathToFile) throws Exception { + if (!pathToFile.toFile().exists()) { + return; + } + if (pathToFile.toFile().isFile()) { + if (!pathToFile.toFile().delete()) { + ErrorFunctions.smthWrong("drop"); + } + } + try { + if (pathToFile.toFile().isDirectory()) { + String[] names = pathToFile.toFile().list(); + if (names.length != 0) { + for (String name : names) { + if (FileUtils.isDirectory(pathToFile.toString(), name)) { + recursiveDrop(Paths.get(pathToFile.toString(), name).normalize()); + } else { + if (!FileUtils.delete(pathToFile.toString(), name)) { + ErrorFunctions.smthWrong("drop"); + } + } + } + } + if (!pathToFile.toFile().delete()) { + ErrorFunctions.smthWrong("drop"); + } + } + } catch (SecurityException secException) { + ErrorFunctions.security("drop", secException.getMessage()); + } + } + + private Set readOldTables(final Path path) throws Exception { + Set setOfNames = new HashSet(); + String[] names = path.toFile().list(); + try { + for (String name : names) { + if (FileUtils.isDirectory(path.toString(), name)) { + Table temp = new TableByVolodden(name); + temp.size(); + setOfNames.add(name); + } else { + Files.delete(Paths.get(dbPath, name)); + } + } + return setOfNames; + } catch (Exception exception) { + ErrorFunctions.smthWrong("read"); + return null; + } + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableProviderFactoryByVolodden.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableProviderFactoryByVolodden.java new file mode 100644 index 000000000..7b9c18fcf --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/database/TableProviderFactoryByVolodden.java @@ -0,0 +1,21 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.database; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.TableProviderFactory; + +import java.io.IOException; + +public class TableProviderFactoryByVolodden implements TableProviderFactory{ + + @Override + public TableProviderByVolodden create(String dir) throws IOException { + if (dir == null) { + throw new IllegalArgumentException("wrong directory"); + } + + try { + return new TableProviderByVolodden(dir); + } catch (Exception e) { + throw new IOException("IO wrong."); + } + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseCorruptedException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseCorruptedException.java new file mode 100644 index 000000000..48f4969b9 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseCorruptedException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class DatabaseCorruptedException extends Exception { + public DatabaseCorruptedException() { + super(); + } + + public DatabaseCorruptedException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseReadErrorException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseReadErrorException.java new file mode 100644 index 000000000..4f8a61404 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseReadErrorException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class DatabaseReadErrorException extends Exception { + public DatabaseReadErrorException() { + super(); + } + + public DatabaseReadErrorException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseWriteErrorException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseWriteErrorException.java new file mode 100644 index 000000000..db478b8d2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/DatabaseWriteErrorException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class DatabaseWriteErrorException extends Exception { + public DatabaseWriteErrorException() { + super(); + } + + public DatabaseWriteErrorException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/InterpreterInvalidCommandNameException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/InterpreterInvalidCommandNameException.java new file mode 100644 index 000000000..4c93132db --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/InterpreterInvalidCommandNameException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class InterpreterInvalidCommandNameException extends Exception { + public InterpreterInvalidCommandNameException() { + super(); + } + + public InterpreterInvalidCommandNameException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/ProhibitedAccessException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/ProhibitedAccessException.java new file mode 100644 index 000000000..4f45b49b1 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/ProhibitedAccessException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class ProhibitedAccessException extends Exception { + public ProhibitedAccessException() { + super(); + } + + public ProhibitedAccessException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/SomethingWrongException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/SomethingWrongException.java new file mode 100644 index 000000000..6139b16ae --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/SomethingWrongException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class SomethingWrongException extends Exception { + public SomethingWrongException() { + super(); + } + + public SomethingWrongException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/WrongInputException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/WrongInputException.java new file mode 100644 index 000000000..15ab241ca --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/WrongInputException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class WrongInputException extends Exception { + public WrongInputException() { + super(); + } + + public WrongInputException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/WrongQuantityOfArgumentsException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/WrongQuantityOfArgumentsException.java new file mode 100644 index 000000000..c3a6e7c5c --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/exceptions/WrongQuantityOfArgumentsException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions; + +public class WrongQuantityOfArgumentsException extends Exception { + public WrongQuantityOfArgumentsException() { + super(); + } + + public WrongQuantityOfArgumentsException(final String message) { + super(message); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/Command.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/Command.java new file mode 100644 index 000000000..e826b0fae --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/Command.java @@ -0,0 +1,20 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter; + + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +public abstract class Command { + + protected String name; + protected int quantityOfArguments; + + protected static void tableIsNull(final Table table) throws Exception { + if (table == null) { + System.out.print("no table"); + throw new Exception(); + } + } + + public abstract CommandHandler create(); + +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/CommandHandler.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/CommandHandler.java new file mode 100644 index 000000000..9b5fa51b9 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/CommandHandler.java @@ -0,0 +1,48 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.ErrorFunctions; + +import java.util.function.BiConsumer; + +public class CommandHandler { + + private String name; + private int quantityOfArguments; + private BiConsumer callback; + + public CommandHandler(String name, int quantityOfArguments, + BiConsumer callback) { + this.name = name; + this.quantityOfArguments = quantityOfArguments; + this.callback = callback; + } + + public CommandHandler(final String name, final int quantityOfArguments) { + this.name = name; + this.quantityOfArguments = quantityOfArguments; + } + + public String getName() { + return name; + } + + private void checkQuantityOfArguments(final String[] args, final String commandName) throws Exception { + if (((args.length != quantityOfArguments) && (quantityOfArguments != -1)) + || (args.length < 2) && (quantityOfArguments == -1)) { + ErrorFunctions.wrongQuantityOfArguments(commandName); + } + if (args.length > 1) { + for (int i = 1; i < args.length; ++i) { + if (args[i].isEmpty()) { + ErrorFunctions.wrongInput(commandName); + } + } + } + } + + public void execute(InterpreterState interpreterState, String[] args) throws Exception { + checkQuantityOfArguments(args, name); + + callback.accept(interpreterState, args); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/Interpreter.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/Interpreter.java new file mode 100644 index 000000000..45a3ccaf2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/Interpreter.java @@ -0,0 +1,70 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.ReturnCodes; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +public class Interpreter { + + private final String promt = "$ "; + private final String statementDelimiter = ";"; + private final String paramRegExp = "\\s+"; + + private final Map commands; + private final InterpreterState interpreterState; + + public Interpreter(InterpreterState interpreter, CommandHandler[] comms) { + commands = new HashMap(); + interpreterState = interpreter; + for (CommandHandler command : comms) { + commands.put(command.getName(), command); + } + } + + private void executeLine(final String[] input) { + for (int i = 0; i < input.length; ++i) { + if (input[i].length() > 0) { + String[] buffer = input[i].trim().split(paramRegExp); + String commandName = buffer[0]; + if (commandName.equals("exit")) { + System.exit(ReturnCodes.SUCCESS); + } + CommandHandler command = commands.get(commandName); + if (command == null) { + System.err.println("Command does not exist: [" + commandName + "]"); + return; + } + try { + command.execute(interpreterState, Arrays.copyOfRange(buffer, 1, buffer.length)); + } catch (Exception exception) { + System.err.println(exception.getMessage()); + } + } + } + } + + public void run(String[] args) { + while (args.length == 0) { + try (Scanner scanner = new Scanner(System.in)) { + System.out.print(promt); + String[] input = scanner.nextLine().split(statementDelimiter); + executeLine(input); + } catch (Throwable exception) { + System.err.println("Smth wrong: " + exception.getMessage()); + System.exit(ReturnCodes.ERROR); + } + } + if (args.length == 0) { + return; + } + StringBuilder helpArray = new StringBuilder(); + for (int i = 0; i < args.length; ++i) { + helpArray.append(args[i]).append(' '); + } + String[] input = helpArray.toString().split(statementDelimiter); + executeLine(input); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/InterpreterState.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/InterpreterState.java new file mode 100644 index 000000000..ffcc646ad --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/InterpreterState.java @@ -0,0 +1,5 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter; + +public interface InterpreterState { + +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderCreate.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderCreate.java new file mode 100644 index 000000000..25a479ec2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderCreate.java @@ -0,0 +1,48 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.database.JSONUtils; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.TableProvider; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class CommandProviderCreate extends Command { + + public CommandProviderCreate() { + name = "create"; + quantityOfArguments = -1; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + TableProvider tableProvider = state.getTableProvider(); + + List> list = new ArrayList>(); + for (int i = 1; i < args.length; ++i) { + list.add(JSONUtils.stringToType(args[i])); + } + try { + tableProvider.getTable(args[0]); + System.out.println(name + " exists"); + } catch (IllegalArgumentException e) { + Table table = null; + try { + table = tableProvider.createTable(args[0], list); + } catch (IOException e1) { + e1.printStackTrace(); + } + if (table != null) { + System.out.println("created"); + } + } + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderDrop.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderDrop.java new file mode 100644 index 000000000..c38941b77 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderDrop.java @@ -0,0 +1,36 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.TableProvider; + +import java.io.IOException; + +public class CommandProviderDrop extends Command { + + public CommandProviderDrop() { + name = "drop"; + quantityOfArguments = 1; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + TableProvider tableProvider = state.getTableProvider(); + + try { + tableProvider.removeTable(args[0]); + System.out.println("dropped"); + } catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); + } catch (IllegalStateException e) { + System.err.println(e.getMessage()); + } catch (IOException e) { + System.err.println(e.getMessage()); + } + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderUse.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderUse.java new file mode 100644 index 000000000..89764c85e --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandProviderUse.java @@ -0,0 +1,37 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +public class CommandProviderUse extends Command { + + public CommandProviderUse() { + name = "use"; + quantityOfArguments = 1; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + if (table.getName().equals(args[0])) { + System.out.println("[" + args[0] + "] already used"); + return; + } + int changes = table.getNumberOfUncommittedChanges(); + if (changes != 0) { + System.out.println(changes + " unsaved changes"); + return; + } + try { + state.setTable(state.getTableProvider().getTable(args[0])); + } catch (Exception e) { + System.out.println(e.getMessage()); + } + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableCommit.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableCommit.java new file mode 100644 index 000000000..42f3e7df9 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableCommit.java @@ -0,0 +1,37 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +import java.io.IOException; + +public class CommandTableCommit extends Command { + + public CommandTableCommit() { + name = "commit"; + quantityOfArguments = 0; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + try { + tableIsNull(table); + } catch (Exception e) { + return; + } + + try { + System.out.println(table.commit()); + } catch (IOException e) { + e.printStackTrace(); + } + state.setTable(table); + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableGet.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableGet.java new file mode 100644 index 000000000..00dbf9a5e --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableGet.java @@ -0,0 +1,38 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.database.JSONUtils; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Storeable; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +public class CommandTableGet extends Command { + + public CommandTableGet() { + name = "get"; + quantityOfArguments = 1; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + try { + tableIsNull(table); + } catch (Exception e) { + return; + } + + Storeable value = table.get(args[0]); + if (value == null) { + System.out.println("not found"); + } else { + System.out.println("found"); + System.out.println(JSONUtils.storeableToString(table, value)); + } + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableList.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableList.java new file mode 100644 index 000000000..6123aa65b --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableList.java @@ -0,0 +1,41 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +import java.util.List; + +public class CommandTableList extends Command { + + public CommandTableList() { + name = "list"; + quantityOfArguments = 0; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + try { + tableIsNull(table); + } catch (Exception e) { + return; + } + + List list = table.list(); + int i = list.size(); + for (String key : list) { + System.out.print(key); + --i; + if (i > 0) { + System.out.print(", "); + } + } + System.out.println(); + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTablePut.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTablePut.java new file mode 100644 index 000000000..694d88644 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTablePut.java @@ -0,0 +1,44 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.database.JSONUtils; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.database.StoreableByVolodden; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Storeable; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +import java.util.Arrays; + +public class CommandTablePut extends Command { + + public CommandTablePut() { + name = "put"; + quantityOfArguments = -1; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + try { + tableIsNull(table); + } catch (Exception e) { + return; + } + + String[] buffer = Arrays.copyOfRange(args, 1, args.length); + Storeable newValue = new StoreableByVolodden(table, buffer); + Storeable value = table.put(args[0], newValue); + if (value == null) { + System.out.println("new"); + } else { + System.out.println("overwrite"); + System.out.println(JSONUtils.storeableToString(table, value)); + } + state.setTable(table); + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableRemove.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableRemove.java new file mode 100644 index 000000000..a5448249a --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableRemove.java @@ -0,0 +1,37 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Storeable; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +public class CommandTableRemove extends Command { + + public CommandTableRemove() { + name = "remove"; + quantityOfArguments = 1; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + try { + tableIsNull(table); + } catch (Exception e) { + return; + } + + Storeable value = table.remove(args[0]); + if (value == null) { + System.out.println("not found"); + } else { + System.out.println("removed"); + } + state.setTable(table); + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableRollback.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableRollback.java new file mode 100644 index 000000000..d7ef5a930 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableRollback.java @@ -0,0 +1,31 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +public class CommandTableRollback extends Command { + + public CommandTableRollback() { + name = "rollback"; + quantityOfArguments = 1; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + try { + tableIsNull(table); + } catch (Exception e) { + return; + } + + System.out.println(table.rollback()); + state.setTable(table); + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableSize.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableSize.java new file mode 100644 index 000000000..83c09a75e --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/interpreter/commands/CommandTableSize.java @@ -0,0 +1,30 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Command; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.main.StringInterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; + +public class CommandTableSize extends Command { + + public CommandTableSize() { + name = "size"; + quantityOfArguments = 0; + } + + @Override + public CommandHandler create() { + return new CommandHandler(name, quantityOfArguments, + (interpreterState, args) -> { + StringInterpreterState state = (StringInterpreterState) interpreterState; + Table table = state.getTable(); + try { + tableIsNull(table); + } catch (Exception e) { + return; + } + + System.out.println(table.size()); + }); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/ErrorFunctions.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/ErrorFunctions.java new file mode 100644 index 000000000..ee8c1c9c8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/ErrorFunctions.java @@ -0,0 +1,59 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.main; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.exceptions.*; + +public class ErrorFunctions { + + public static void errorRead() throws DatabaseReadErrorException { + throw new DatabaseReadErrorException("error reading from file"); + } + + public static void errorWrite() throws DatabaseWriteErrorException { + throw new DatabaseWriteErrorException("error writing to file"); + } + + public static void invalidName(final String commandName, final String arg) + throws InterpreterInvalidCommandNameException { + throw new InterpreterInvalidCommandNameException(commandName + ": [" + arg + "] is invalid name."); + } + + public static void nameIsNull(final String commandName, final String arg) throws IllegalArgumentException { + throw new IllegalArgumentException(commandName + ": [" + arg + "] is null."); + } + + public static void notDirectory(final String commandName, final String arg) throws IllegalStateException { + throw new IllegalStateException(commandName + ": [" + arg + "] is not a directory."); + } + + public static void notExists(final String commandName, final String arg) throws IllegalStateException { + throw new IllegalStateException(commandName + ": [" + arg + "] does not exists."); + } + + public static void notMkdir(final String commandName, final String arg) throws IllegalArgumentException { + throw new IllegalArgumentException(commandName + ": failed to create a directory [" + arg + "]."); + } + + public static void security(final String commandName, final String arg) throws ProhibitedAccessException { + throw new ProhibitedAccessException(commandName + ": access to the [" + arg + "] is prohibited."); + } + + public static void smthWrong(final String commandName) throws SomethingWrongException { + throw new SomethingWrongException(commandName + "something gone wrong."); + } + + public static void smthWrong(final String commandName, final String message) throws SomethingWrongException { + throw new SomethingWrongException(commandName + " :" + message); + } + + public static void tableNameIsFile(final String commandName, final String arg) throws IllegalArgumentException { + throw new IllegalArgumentException(commandName + ": [" + arg + "] is file"); + } + + public static void wrongInput(final String commandName) throws WrongInputException { + throw new WrongInputException(commandName + ": wrong input."); + } + + public static void wrongQuantityOfArguments(final String commandName) throws WrongQuantityOfArgumentsException { + throw new WrongQuantityOfArgumentsException(commandName + ": wrong quantity of arguments."); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/Main.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/Main.java new file mode 100644 index 000000000..78f5d6c08 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/Main.java @@ -0,0 +1,38 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.main; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.database.TableProviderFactoryByVolodden; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.CommandHandler; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.Interpreter; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.InterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.commands.*; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.TableProvider; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.TableProviderFactory; + +public class Main { + + public static void main(final String[] args) { + try { + TableProviderFactory tpf = new TableProviderFactoryByVolodden(); + String dir = System.getProperty("fizteh.db.dir"); + TableProvider tableProvider = tpf.create(dir); + InterpreterState interpreterState = new StringInterpreterState(tableProvider); + new Interpreter(interpreterState, + new CommandHandler[] { + new CommandTableGet().create(), + new CommandTableList().create(), + new CommandTablePut().create(), + new CommandTableRemove().create(), + new CommandTableSize().create(), + new CommandTableCommit().create(), + new CommandProviderCreate().create(), + new CommandProviderDrop().create(), + new CommandTableCommit().create(), + new CommandProviderUse().create()}) + .run(args); + } catch (Exception exception) { + System.err.println(exception.getMessage()); + System.exit(ReturnCodes.ERROR); + } + System.exit(ReturnCodes.SUCCESS); + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/ReturnCodes.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/ReturnCodes.java new file mode 100644 index 000000000..61dabf3eb --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/ReturnCodes.java @@ -0,0 +1,6 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.main; + +public class ReturnCodes { + public static final int SUCCESS = 0; + public static final int ERROR = 1; +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/StringInterpreterState.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/StringInterpreterState.java new file mode 100644 index 000000000..13d3b2d94 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/main/StringInterpreterState.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.main; + +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.interpreter.InterpreterState; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.Table; +import ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured.TableProvider; + +public class StringInterpreterState implements InterpreterState { + private TableProvider tableProvider; + private Table table; + + public StringInterpreterState(TableProvider tableProvider) { + this.tableProvider = tableProvider; + } + + public TableProvider getTableProvider() { + return tableProvider; + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/ColumnFormatException.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/ColumnFormatException.java new file mode 100644 index 000000000..13a759179 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/ColumnFormatException.java @@ -0,0 +1,23 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +/** + * Бросается при попытке извлечь из колонки {@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/Volodin_Denis/Parallel/structured/Index.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/Index.java new file mode 100644 index 000000000..18cf082aa --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/Index.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +/** + * Представляет базу для интерфейс для работы с таблицей, содержащей ключи-значения. + * Используется также для доступа к поисковому индексу. + */ +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/Volodin_Denis/Parallel/structured/IndexProvider.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/IndexProvider.java new file mode 100644 index 000000000..8eb9a7d19 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/IndexProvider.java @@ -0,0 +1,30 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +/** + * Дополнительный интерфейс к {@link ru.fizteh.fivt.storage.structured.TableProvider}, позволяющий создавать индексы на + * основе имеющейся таблицы. + */ +public interface IndexProvider extends TableProvider { + /** + * + * Возвращает индекс с таким именем, если он существует. + * + * @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/Volodin_Denis/Parallel/structured/RemoteTableProvider.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/RemoteTableProvider.java new file mode 100644 index 000000000..87f6733c8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/RemoteTableProvider.java @@ -0,0 +1,9 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +import java.io.Closeable; + +/** + * Расширенный интерфейс {@link TableProvider}, с предоставлением метода {@link #close()}. + */ +public interface RemoteTableProvider extends TableProvider, Closeable { +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/RemoteTableProviderFactory.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/RemoteTableProviderFactory.java new file mode 100644 index 000000000..9b5765c08 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/RemoteTableProviderFactory.java @@ -0,0 +1,22 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +import java.io.IOException; + +/** + * Представляет интерфейс для подключения к удаленному хранилищу. + * + * Предполагается, что реализация интерфейса клиента будет иметь публичный конструктор без параметров. + */ +public interface RemoteTableProviderFactory { + /** + * Возвращает {@link RemoteTableProvider} для работы с удаленных хранилищем. + * + * @param hostname Имя узла, на котором работает TCP-сервер хранилища. + * @param port Порт, на котором работает TCP-сервер хранилища. + * @return Remote-{@link RemoteTableProvider}, который подключен к TCP-серверу хранилища по заданному адресу. + * + * @throws IllegalArgumentException В случае некорректных входных параметров. + * @throws java.io.IOException В случае ошибок ввода/вывода. + */ + RemoteTableProvider connect(String hostname, int port) throws IOException; +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/Storeable.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/Storeable.java new file mode 100644 index 000000000..2d50e5b4e --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/Storeable.java @@ -0,0 +1,96 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +/** + * Список фиксированной структуры, строка таблицы {@link Table}. + * + * Нумерация колонок с нуля. Позиция в списке соответствует колонке таблицы под тем же номером. + * + * С помощью {@link TableProvider} может быть сериализован или десериализован. + * + * Для получения объекта из нужной колонки воспользуйтесь соответствующим геттером. + * Для установки объекта а колонку воспользуйтесь {@link #setColumnAt(int, Object)} . + */ +public interface Storeable { + + /** + * Установить значение в колонку + * @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/Volodin_Denis/Parallel/structured/Table.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/Table.java new file mode 100644 index 000000000..37584e91c --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/Table.java @@ -0,0 +1,92 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +import java.io.IOException; +import java.util.List; + +/** + * Представляет интерфейс для работы с таблицей, содержащей ключи-значения. Ключи должны быть уникальными. + * + * Транзакционность: изменения фиксируются или откатываются с помощью методов {@link #commit()} или {@link #rollback()}, + * соответственно. Предполагается, что между вызовами этих методов никаких операций ввода-вывода не происходит. + * + * Данный интерфейс не является потокобезопасным. + */ +public interface Table extends Index { + + /** + * Устанавливает значение по указанному ключу. + * + * @param key Ключ для нового значения. Не может быть null. + * @param value Новое значение. Не может быть null. + * @return Значение, которое было записано по этому ключу ранее. Если ранее значения не было записано, + * возвращает null. + * + * @throws IllegalArgumentException Если значение параметров key или value является null. + * @throws ColumnFormatException - при попытке передать Parallel с колонками другого типа. + */ + Storeable put(String key, Storeable value) throws ColumnFormatException; + + /** + * Удаляет значение по указанному ключу. + * + * @param key Ключ для поиска значения. Не может быть null. + * @return Предыдущее значение. Если не найдено, возвращает null. + * + * @throws IllegalArgumentException Если значение параметра key является null. + */ + Storeable 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/Volodin_Denis/Parallel/structured/TableProvider.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/TableProvider.java new file mode 100644 index 000000000..b7c0a99ae --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/TableProvider.java @@ -0,0 +1,106 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +import java.io.IOException; +import java.text.ParseException; +import java.util.List; + +/** + * Управляющий класс для работы с {@link Table таблицами} + * + * Предполагает, что актуальная версия с устройства хранения, сохраняется при создании + * экземпляра объекта. Далее ввод-вывод выполняется только в момент создания и удаления + * таблиц. + * + * Данный интерфейс не является потокобезопасным. + */ +public interface TableProvider { + + /** + * Возвращает таблицу с указанным названием. + * + * Последовательные вызовы метода с одинаковыми аргументами должны возвращать один и тот же объект таблицы, + * если он не был удален с помощью {@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 Storeable}, соответствующий структуре таблицы. + * + * @param table Таблица, которой должен принадлежать {@link Storeable}. + * @param value Строка, из которой нужно прочитать {@link Storeable}. + * @return Прочитанный {@link Storeable}. + * + * @throws ParseException - при каких-либо несоответстиях в прочитанных данных. + */ + Storeable deserialize(Table table, String value) throws ParseException; + + /** + * Преобразовывает объект {@link Storeable} в строку. + * + * @param table Таблица, которой должен принадлежать {@link Storeable}. + * @param value {@link Storeable}, который нужно записать. + * @return Строка с записанным значением. + * + * @throws ColumnFormatException При несоответствии типа в {@link Storeable} и типа колонки в таблице. + */ + String serialize(Table table, Storeable value) throws ColumnFormatException; + + /** + * Создает новый пустой {@link Storeable} для указанной таблицы. + * + * @param table Таблица, которой должен принадлежать {@link Storeable}. + * @return Пустой {@link Storeable}, нацеленный на использование с этой таблицей. + */ + Storeable createFor(Table table); + + /** + * Создает новый {@link Storeable} для указанной таблицы, подставляя туда переданные значения. + * + * @param table Таблица, которой должен принадлежать {@link Storeable}. + * @param values Список значений, которыми нужно проинициализировать поля Parallel. + * @return {@link Storeable}, проинициализированный переданными значениями. + * @throws ColumnFormatException При несоответствии типа переданного значения и колонки. + * @throws IndexOutOfBoundsException При несоответствии числа переданных значений и числа колонок. + */ + Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException; + + /** + * Возвращает имена существующих таблиц, которые могут быть получены с помощью {@link #getTable(String)}. + * + * @return Имена существующих таблиц. + */ + List getTableNames(); +} diff --git a/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/TableProviderFactory.java b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/TableProviderFactory.java new file mode 100644 index 000000000..e10921970 --- /dev/null +++ b/src/ru/fizteh/fivt/students/Volodin_Denis/Parallel/structured/TableProviderFactory.java @@ -0,0 +1,22 @@ +package ru.fizteh.fivt.students.Volodin_Denis.Parallel.structured; + +import java.io.IOException; + +/** + * Представляет интерфейс для создание экземпляров {@link TableProvider}. + * + * Предполагается, что реализация интерфейса фабрики будет иметь публичный конструктор без параметров. + */ +public interface TableProviderFactory { + + /** + * Возвращает объект для работы с базой данных. + * + * @param path Директория с файлами базы данных. + * @return Объект для работы с базой данных, который будет работать в указанной директории. + * + * @throws IllegalArgumentException Если значение директории null или имеет недопустимое значение. + * @throws java.io.IOException В случае ошибок ввода/вывода. + */ + TableProvider create(String path) throws IOException; +}