From e836ceee94b4d87ec55ac3004ef2fbfa645bc486 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sat, 22 Nov 2014 22:20:50 +0300 Subject: [PATCH 01/36] Begin to do Storeable task. Something (can't build now, nothing works). This commit was made to make it possible to switch branches to do some refactoring. --- .../LebedevAleksey/storeable/Database.java | 162 ++++++++++++++++++ .../storeable/DatabaseState.java | 18 ++ .../LebedevAleksey/storeable/Main.java | 130 ++++++++++++++ .../LebedevAleksey/storeable/Storeable.java | 58 +++++++ .../storeable/StoreableTable.java | 160 +++++++++++++++++ 5 files changed, 528 insertions(+) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java new file mode 100644 index 000000000..9716a785f --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -0,0 +1,162 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.ParseException; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class Database implements TableProvider { + public static final String TABLE_SIGNATURE_FILE_NAME = "signature.tsv"; + private static final String INCORRECT_NAME_OF_TABLES = "This name is not correct, folder can't be created"; + private static Map> stringTypesMap = new TreeMap<>(); + private static Map, String> typesStringMap = new TreeMap<>(); + + static { + stringTypesMap.put("int", int.class); + stringTypesMap.put("long", long.class); + stringTypesMap.put("byte", byte.class); + stringTypesMap.put("float", float.class); + stringTypesMap.put("double", double.class); + stringTypesMap.put("boolean", boolean.class); + stringTypesMap.put("String", String.class); + typesStringMap.put(int.class, "int"); + typesStringMap.put(long.class, "long"); + typesStringMap.put(byte.class, "byte"); + typesStringMap.put(float.class, "float"); + typesStringMap.put(double.class, "double"); + typesStringMap.put(boolean.class, "boolean"); + typesStringMap.put(String.class, "String"); + + } + + private final Path directoryPath; + private Map tables = new TreeMap<>(); + + + public Database(String directory) { + directoryPath = new File(directory).toPath(); + } + + private void assertNameNotNull(String name) { + if (name == null) { + throw new IllegalArgumentException("Argument \"name\" is null"); + } + } + + @Override + public Table getTable(String name) { + assertNameNotNull(name); + return tables.get(name); + } + + @Override + public Table createTable(String name, List> columnTypes) throws IOException { + Table checkExists = getTable(name); + if (checkExists == null) { + Path rootDirectoryPath = getRootDirectoryPath(); + Path path = rootDirectoryPath.resolve(name); + if (path.startsWith(rootDirectoryPath) && path.getParent().equals(rootDirectoryPath)) { + if (columnTypes == null) { + throw new IllegalArgumentException("Argument \"columnTypes\" is null."); + } + String tableSignature = createTableSignature(columnTypes); + try { + Files.createDirectory(path); + try (FileOutputStream stream = new FileOutputStream(path.resolve(TABLE_SIGNATURE_FILE_NAME).toString())) { + try (DataOutputStream dataStream = new DataOutputStream(stream)) { + dataStream.writeUTF(tableSignature); + dataStream.flush(); + } + } + } catch (Exception ex) { + try { + Files.delete(path); + } catch (Throwable suppressed) { + ex.addSuppressed(suppressed); + } + throw ex; + } + StoreableTable table = generateTable(name); + tables.put(name, table); + return table; + } else { + throw new IllegalArgumentException(INCORRECT_NAME_OF_TABLES); + } + } else { + return null; + } + } + + private String createTableSignature(List> columnTypes) { + String tableSignature = ""; + for (Class column : columnTypes) { + if (column == null) { + throw new IllegalArgumentException("Null column"); + } + Class type = stringTypesMap.get(column); + if (type == null) { + throw new IllegalArgumentException("Type is not supported"); + } + tableSignature += type; + tableSignature += " "; + } + tableSignature = tableSignature.trim(); + return tableSignature; + } + + private Path getRootDirectoryPath() { + return directoryPath; + } + + private StoreableTable generateTable(String name) { + return null;//TODO + } + + @Override + public void removeTable(String name) throws IOException { + StoreableTable table = (StoreableTable) getTable(name); + table.drop(); + tables.remove(table); + Files.delete(directoryPath.resolve(name)); + } + + @Override + public Storeable deserialize(Table table, String value) throws ParseException { + //TODO + throw new NotImplementedException(); + return null; + } + + @Override + public String serialize(Table table, Storeable value) throws ColumnFormatException { + //TODO + throw new NotImplementedException(); + return null; + } + + @Override + public Storeable createFor(Table table) { + //TODO + throw new NotImplementedException(); + return null; + } + + @Override + public Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException { + //TODO + throw new NotImplementedException(); + return null; + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java new file mode 100644 index 000000000..58e4897b0 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable; + +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +public class DatabaseState extends ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.InterpreterState { + protected Database database; + + public DatabaseState() { + //TODO + throw new NotImplementedException(); + String directoryPath = System.getProperty("fizteh.db.dir"); + if (directoryPath == null) { + //throw ... + } else { + database = new Database(directoryPath); + } + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java new file mode 100644 index 000000000..ff4004824 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java @@ -0,0 +1,130 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable; + +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.*; + +import java.util.ArrayList; +import java.util.List; + +public class Main { + + public static final String USE_COMMAND = "use"; + public static final boolean NO_TABLE_RETURN_CODE = true; + public static final String EXIT_COMMAND = "exit"; + + public static void main(String[] args) { + try { + DatabaseState state = new DatabaseState(); + List commands = new ArrayList<>(MultiFileHashMap.getCommands()); + for (int i = 0; i < commands.size(); i++) { + if (commands.get(i).getName().equals(USE_COMMAND)) { + commands.set(i, new Command(USE_COMMAND, 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + StoreableTable currentTable; + int changesCount = 0; + currentTable = getCurrentTableWithoutMessages(state); + if (currentTable != null) { + changesCount = currentTable.changesCount(); + } + if (changesCount == 0) { + String name = arguments[0]; + try { + MultiFileHashMap.toDatabaseState(state).getDatabase().useTable(name); + System.out.println("using " + name); + } catch (TableNotFoundException ex) { + System.out.println(name + " not exists"); + } catch (DatabaseFileStructureException | LoadOrSaveException e) { + System.err.println(e.getMessage()); + return false; + } + } else { + System.out.println(changesCount + " unsaved changes"); + } + return true; + } + }); + } + if (commands.get(i).getName().equals(EXIT_COMMAND)) { + commands.set(i, new Command(EXIT_COMMAND, 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + StoreableTable currentTable = getCurrentTableWithoutMessages(state); + int changesCount = 0; + if (currentTable != null) { + changesCount = currentTable.changesCount(); + } + if (changesCount == 0) { + state.exit(); + return false; + } else { + System.out.println(changesCount + " unsaved changes"); + } + return true; + } + }); + } + } + commands.add(new Command("commit", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + try { + System.out.println(getCurrentTable(state).commit()); + return true; + } catch (DatabaseFileStructureException | LoadOrSaveException e) { + System.err.println(e.getMessage()); + return false; + } catch (TableNotFoundException e) { + return true; + } + } + }); + commands.add(new Command("rollback", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + try { + System.out.println(getCurrentTable(state).rollback()); + } catch (TableNotFoundException e) { + return true; + } + return NO_TABLE_RETURN_CODE; + } + }); + commands.add(new Command("size", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + try { + System.out.println(getCurrentTable(state).count()); + return true; + } catch (LoadOrSaveException | DatabaseFileStructureException e) { + System.err.println(e.getMessage()); + return false; + } catch (TableNotFoundException e) { + return NO_TABLE_RETURN_CODE; + } + } + }); + runDatabaseInterpreter(args, state, commands); + } catch (DatabaseFileStructureException | LoadOrSaveException ex) { + System.err.println(ex.getMessage()); + System.exit(4); + } + } + + private static StoreableTable getCurrentTableWithoutMessages(InterpreterState state) { + return (StoreableTable) ((DatabaseState) state).getDatabase().getCurrentTable(); + } + + private static StoreableTable getCurrentTable(InterpreterState state) throws TableNotFoundException { + return ((StoreableTable) MultiFileHashMap.getCurrentTable(state)); + } + + private static void runDatabaseInterpreter(String[] args, DatabaseState state, List commands) { + Interpreter interpreter = new Interpreter(commands, state); + interpreter.run(args); + if (args.length != 0) { + if (!state.tryToSave()) { + System.exit(2); + } + } + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java new file mode 100644 index 000000000..09e64cdd8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java @@ -0,0 +1,58 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; + +public class Storeable implements ru.fizteh.fivt.storage.structured.Storeable { + private Object[] data; + private StoreableTable formatTable; + + public Storeable(Object[] data, StoreableTable table) { + this.data = data; + this.formatTable = table; + } + + @Override + public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { + + } + + @Override + public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { + return null; + } + + @Override + public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return null; + } + + @Override + public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return null; + } + + @Override + public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return null; + } + + @Override + public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return null; + } + + @Override + public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return null; + } + + @Override + public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return null; + } + + @Override + public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + return null; + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java new file mode 100644 index 000000000..00e3ce094 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java @@ -0,0 +1,160 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.LoadOrSaveException; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Table; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +import java.util.*; +import java.util.function.Consumer; + +public class StoreableTable implements ru.fizteh.fivt.storage.structured.Table { + private Map changedKeys = new TreeMap<>(); + + private Table stringTable; + + public StoreableTable(String name, ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Database databaseParent) { + super(name, databaseParent); + } + + @Override + public Storeable put(String key, Storeable value) throws ColumnFormatException { + return null; + } + + @Override + public boolean remove(String key) throws LoadOrSaveException, DatabaseFileStructureException { + String value = super.get(key); + String oldValue = get(key); + if (value != null) { + if (oldValue != null) { + changedKeys.put(key, null); + } + } else { + changedKeys.remove(key); + } + return (oldValue != null); + } + + @Override + public int size() { + return 0; + } + + public String getAndRemove(String key) throws DatabaseFileStructureException, LoadOrSaveException { + String oldValue = get(key); + remove(key); + return oldValue; + } + + @Override + public String getName() { + return null; + } + + @Override + public String get(String key) throws LoadOrSaveException, DatabaseFileStructureException { + if (changedKeys.containsKey(key)) { + return changedKeys.get(key); + } else { + return super.get(key); + } + } + + @Override + public String put(String key, String value) throws LoadOrSaveException, DatabaseFileStructureException { + String oldValue = get(key); + if (value.equals(super.get(key))) { + changedKeys.remove(key); + } else { + changedKeys.put(key, value); + } + return oldValue; + } + + @Override + public void save() throws LoadOrSaveException, DatabaseFileStructureException { + for (String key : changedKeys.keySet()) { + String value = changedKeys.get(key); + if (value == null) { + super.remove(key); + } else { + super.put(key, value); + } + } + changedKeys.clear(); + super.save(); + } + + @Override + public int count() throws LoadOrSaveException, DatabaseFileStructureException { + int deletedCount = 0; + int addedCount = 0; + for (String key : changedKeys.keySet()) { + String value = changedKeys.get(key); + if (value == null) { + ++deletedCount; + } else { + if (super.get(key) == null) { + addedCount++; + } + } + } + return super.count() + addedCount - deletedCount; + } + + @Override + public ArrayList list() throws LoadOrSaveException, DatabaseFileStructureException { + Set items = new TreeSet<>(super.list()); + for (String key : changedKeys.keySet()) { + String value = changedKeys.get(key); + if (value == null) { + items.remove(key); + } else { + items.add(key); + } + } + ArrayList result = new ArrayList<>(items.size()); + items.forEach(new Consumer() { + @Override + public void accept(String s) { + result.add(s); + } + }); + return result; + } + + public int changesCount() { + return changedKeys.size(); + } + + public int commit(){ + int changes = changesCount(); + save(); + return changes; + } + + public int rollback() { + int changes = changesCount(); + changedKeys.clear(); + initParts(); + return changes; + } + + @Override + public int getColumnsCount() { + return 0; + } + + @Override + public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { + return null; + } + + public void drop() { + //TODO + throw new NotImplementedException(); + } +} From 4b0e2b69d0df8e9b769733a9c339ca75fe3330cf Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sat, 22 Nov 2014 22:35:45 +0300 Subject: [PATCH 02/36] JUnit task v1.2.1 (Make some refactoring, to make it possible to do 5th task --- .../LebedevAleksey/MultiFileHashMap/Database.java | 4 ++-- .../students/LebedevAleksey/MultiFileHashMap/Table.java | 8 ++++---- .../fivt/students/LebedevAleksey/junit/Database.java | 4 ++-- .../fizteh/fivt/students/LebedevAleksey/junit/Table.java | 5 +++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Database.java index 2f335f5e0..8205d31a2 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Database.java @@ -139,8 +139,8 @@ public Table createTable(String name) throws TableAlreadyExistsException, LoadOr } } - protected Table generateTable(String name) { - return new Table(name, this); + protected Table generateTable(String name) throws DatabaseFileStructureException { + return new Table(name, getRootDirectoryPath()); } public void removeTable(String name) throws TableNotFoundException, LoadOrSaveException, diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Table.java b/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Table.java index a33d05972..5571ac185 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Table.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/MultiFileHashMap/Table.java @@ -9,12 +9,12 @@ public class Table { protected static final int SUBDIRECTORIES_COUNT = 16; private TablePart[][] structuredParts; private String tableName; - private Database database; + private Path tablePath; - public Table(String name, Database databaseParent) { + public Table(String name, Path path) { initParts(); tableName = name; - database = databaseParent; + tablePath = path; } protected void initParts() { @@ -46,7 +46,7 @@ public String getTableName() { } public Path getDirectory() throws DatabaseFileStructureException { - return database.getRootDirectoryPath().resolve(tableName); + return tablePath.resolve(tableName); } public int count() throws LoadOrSaveException, DatabaseFileStructureException { diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Database.java index f1f163ee0..65aa43ea2 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Database.java @@ -9,7 +9,7 @@ public Database(String path) throws DatabaseFileStructureException, LoadOrSaveEx } @Override - protected Table generateTable(String name) { - return new Table(name, this); + protected Table generateTable(String name) throws DatabaseFileStructureException { + return new Table(name, getRootDirectoryPath()); } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java b/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java index 317cc76d8..104e84e63 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java @@ -3,14 +3,15 @@ import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.LoadOrSaveException; +import java.nio.file.Path; import java.util.*; import java.util.function.Consumer; public class Table extends ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Table { private Map changedKeys = new TreeMap<>(); - public Table(String name, ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Database databaseParent) { - super(name, databaseParent); + public Table(String name, Path tablePath) { + super(name, tablePath); } @Override From 03a98aaea24543d1367ed6a97a66cbf2524c45ec Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 24 Nov 2014 17:39:50 +0300 Subject: [PATCH 03/36] Json in temp folder --- .../temp/json/BrokenJsonException.java | 18 + .../LebedevAleksey/temp/json/JsonParser.java | 413 +++++++++++++++++ .../json/JsonUnsopportedObjectException.java | 18 + .../temp/tests/JsonParserTest.java | 419 ++++++++++++++++++ 4 files changed, 868 insertions(+) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/BrokenJsonException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonParser.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonUnsopportedObjectException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/temp/tests/JsonParserTest.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/BrokenJsonException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/BrokenJsonException.java new file mode 100644 index 000000000..62d2ff930 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/BrokenJsonException.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.LebedevAleksey.temp.json; + +public class BrokenJsonException extends Exception { + public BrokenJsonException() { + } + + public BrokenJsonException(String message) { + super(message); + } + + public BrokenJsonException(String message, Throwable cause) { + super(message, cause); + } + + public BrokenJsonException(Throwable cause) { + super(cause); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonParser.java new file mode 100644 index 000000000..7f3f23cf9 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonParser.java @@ -0,0 +1,413 @@ +package ru.fizteh.fivt.students.LebedevAleksey.temp.json; + +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Pair; + +import java.util.*; + +public abstract class JsonParser { + public static final String UNEXPECTED_STOP_MESSAGE = "Parsers didn't reach the end."; + private static final ThreadLocal NEED_SHORT_INTEGER_TYPES = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return false; + } + }; + + public static void setNeedShortIntegerTypes(boolean needShortIntegerTypes) { + JsonParser.NEED_SHORT_INTEGER_TYPES.set(needShortIntegerTypes); + } + + public static String getJson(Object data) throws JsonUnsopportedObjectException { + StringBuilder result = new StringBuilder(); + createJson(data, result); + return result.toString(); + } + + public static Object parseJson(String json) throws BrokenJsonException { + json = json.trim(); + Pair result = null; + try { + result = parseJson(json, 0, null); + } catch (JsonTerminatedException e) { + throw new BrokenJsonException("Wrong JSON"); + } + if (result.getValue() != json.length()) { + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + return result.getKey(); + } + + public static Pair parseJson(String json, int begin, Character possibleTerminator) + throws BrokenJsonException, JsonTerminatedException { + Character first = null; + List list; + Map map; + int i; + for (i = begin; i < json.length(); i++) { + char symbol = json.charAt(i); + if (possibleTerminator != null && possibleTerminator.equals(symbol)) { + throw new JsonTerminatedException(); + } + switch (symbol) { + case ' ': + break; + case '"': + return parseStringFromJson(json, i + 1); + case '{': + return parseMapFromJson(json, i + 1); + case '[': + return parseArrayFromJson(json, i + 1); + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parseNumberFromJson(json, i); + case 'n': + case 't': + case 'f': + try { + switch (json.substring(i, i + 4)) { + case "null": + return new Pair<>(null, i + 4); + case "true": + return new Pair<>(true, i + 4); + case "fals": + if ("false".equals(json.substring(i, i + 5))) { + return new Pair<>(false, i + 5); + } else { + throw new BrokenJsonException("Unexpected symbol in position " + i + 4); + } + default: + throw new BrokenJsonException("Wrong token"); + } + } catch (IndexOutOfBoundsException e) { + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, e); + } + default: + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i); + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static Pair parseMapFromJson(String json, int begin) throws BrokenJsonException { + Map map = new TreeMap<>(); + int i = begin; + while (i < json.length()) { + Pair result = null; + try { + result = parseJson(json, i, '}'); + } catch (JsonTerminatedException e) { + return searchNextChar(json, map, i, '}'); + } + String key; + try { + key = (String) result.getKey(); + } catch (ClassCastException e) { + throw new BrokenJsonException("Wrong map key"); + } + i = result.getValue(); + boolean exited = false; + for (; i < json.length() && (!exited); ++i) { + char symbol = json.charAt(i); + switch (symbol) { + case ' ': + break; + case ':': + exited = true; + break; + default: + break; + } + } + try { + result = parseJson(json, i, null); + } catch (JsonTerminatedException e) { + throw new BrokenJsonException("JSON is not correct"); + } + map.put(key, result.getKey()); + i = result.getValue(); + exited = false; + for (; i < json.length() && (!exited); ++i) { + char symbol = json.charAt(i); + switch (symbol) { + case ' ': + break; + case ',': + exited = true; + break; + case '}': + return new Pair<>(map, i + 1); + default: + break; + } + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static Pair searchNextChar(String json, Object value, int i, char mask) { + while (json.charAt(i) != mask) { + ++i; + } + return new Pair<>(value, i + 1); + } + + + private static Pair parseArrayFromJson(String json, int begin) throws BrokenJsonException { + List list = new ArrayList<>(); + int i = begin; + while (i < json.length()) { + Pair result = null; + try { + result = parseJson(json, i, ']'); + } catch (JsonTerminatedException e) { + return searchNextChar(json, list, i, ']'); + } + list.add(result.getKey()); + i = result.getValue(); + boolean exited = false; + for (; i < json.length() && (!exited); ++i) { + char symbol = json.charAt(i); + switch (symbol) { + case ' ': + break; + case ',': + exited = true; + break; + case ']': + return new Pair<>(list, i + 1); + default: + break; + } + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static Pair parseNumberFromJson(String json, int begin) throws BrokenJsonException { + List posibleCharcters = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + posibleCharcters.add((char) ('0' + i)); + } + posibleCharcters.add('+'); + posibleCharcters.add('-'); + posibleCharcters.add('E'); + posibleCharcters.add('e'); + posibleCharcters.add('.'); + int i; + for (i = begin; i < json.length(); i++) { + char symbol = json.charAt(i); + if (!posibleCharcters.contains(symbol)) { + break; + } + } + String number = json.substring(begin, i); + if (NEED_SHORT_INTEGER_TYPES.get()) { + try { + byte byteValue = Byte.parseByte(number); + return new Pair<>(byteValue, i); + } catch (NumberFormatException e) { + // Next type... + } + try { + int intValue = Integer.parseInt(number); + return new Pair<>(intValue, i); + } catch (NumberFormatException e) { + // Next type... + } + } + try { + long longValue = Long.parseLong(number); + return new Pair<>(longValue, i); + } catch (NumberFormatException e) { + // Next type... + } + try { + double doubleValue = Double.parseDouble(number); + return new Pair<>(doubleValue, i); + } catch (NumberFormatException e) { + // Next type... + } + throw new BrokenJsonException("Wrong number"); + } + + private static Pair parseStringFromJson(String json, int begin) + throws BrokenJsonException { + StringBuilder builder = new StringBuilder(); + for (int i = begin; i < json.length(); i++) { + char symbol = json.charAt(i); + if (symbol == '\\') { + i = parseSlashSequense(json, builder, i); + } else { + if (symbol == '"') { + return new Pair<>(builder.toString(), i + 1); + } else { + builder.append(symbol); + } + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static int parseSlashSequense(String json, StringBuilder builder, int i) throws BrokenJsonException { + if (i + 1 == json.length()) { + throw new BrokenJsonException("String doesn't finish"); + } + switch (json.charAt(i + 1)) { + case '"': + builder.append("\""); + return i + 1; + case '\\': + builder.append("\\"); + return i + 1; + case '/': + builder.append("/"); + return i + 1; + case 'b': + builder.append("\b"); + return i + 1; + case 'f': + builder.append("\f"); + return i + 1; + case 'n': + builder.append("\n"); + return i + 1; + case 'r': + builder.append("\r"); + return i + 1; + case 't': + builder.append("\t"); + return i + 1; + case 'u': + if (i + 5 >= json.length()) { + throw new BrokenJsonException("Error in \\u in string"); + } + try { + int num = Integer.parseInt(json.substring(i + 2, i + 6), 16); + builder.append((char) num); + return i + 5; + } catch (NumberFormatException e) { + throw new BrokenJsonException("Error in \\u in string: can't parse int", e); + } + default: + throw new BrokenJsonException("Unknown \\ sequence in char number " + i); + } + } + + private static void createJson(Object data, StringBuilder builder) throws JsonUnsopportedObjectException { + if (data == null) { + builder.append("null"); + return; + } + if (containsInterface(data, Map.class)) { + Map map; + try { + map = (Map) data; + } catch (ClassCastException e) { + throw new JsonUnsopportedObjectException("Only Map is supported, when you use map", e); + } + createJson(map, builder); + return; + } + if (containsInterface(data, Iterable.class)) { + createJson((Iterable) data, builder); + return; + } + if (containsInterface(data, Object[].class)) { + createJson((Object[]) data, builder); + return; + } + if (data.getClass() == String.class) { + createJson((String) data, builder); + return; + } + if (data.getClass().getSuperclass() == Number.class) { + builder.append(data.toString()); + return; + } + if (data.getClass() == Boolean.class) { + builder.append(((boolean) data) ? "true" : false); + return; + } + throw new JsonUnsopportedObjectException("This type (" + data.getClass() + ") is unsupported by JSON."); + } + + private static void createJson(String data, StringBuilder builder) { + data = data.replace("\\", "\\\\").replace("\"", "\\\""); + data = data.replace("/", "\\/").replace("\b", "\\b"); + data = data.replace("\f", "\\f").replace("\n", "\\n"); + data = data.replace("\r", "\\r").replace("\t", "\\t"); + builder.append("\""); + builder.append(data); + builder.append("\""); + } + + private static boolean containsInterface(Object data, Class type) { + try { + type.cast(data); + return true; + } catch (ClassCastException e) { + return false; + } + } + + private static void createJson(Iterable data, StringBuilder builder) throws JsonUnsopportedObjectException { + builder.append("["); + boolean first = true; + for (Object item : data) { + if (!first) { + builder.append(","); + } else { + first = false; + } + createJson(item, builder); + } + builder.append("]"); + } + + private static void createJson(Object[] data, StringBuilder builder) throws JsonUnsopportedObjectException { + builder.append("["); + boolean first = true; + for (Object item : data) { + if (!first) { + builder.append(","); + } else { + first = false; + } + createJson(item, builder); + } + builder.append("]"); + } + + private static void createJson(Map data, StringBuilder builder) throws JsonUnsopportedObjectException { + try { + builder.append("{"); + Set keys = data.keySet(); + boolean notFirst = false; + for (String item : keys) { + if (notFirst) { + builder.append(","); + } else { + notFirst = true; + } + createJson(item, builder); + builder.append(":"); + createJson(data.get(item), builder); + } + builder.append("}"); + } catch (ClassCastException e) { + throw new JsonUnsopportedObjectException("Only Map is supported.", e); + } + } +} + +class JsonTerminatedException extends Exception { +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonUnsopportedObjectException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonUnsopportedObjectException.java new file mode 100644 index 000000000..dfb8e168d --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonUnsopportedObjectException.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.LebedevAleksey.temp.json; + +public class JsonUnsopportedObjectException extends Exception { + public JsonUnsopportedObjectException() { + } + + public JsonUnsopportedObjectException(String message) { + super(message); + } + + public JsonUnsopportedObjectException(String message, Throwable cause) { + super(message, cause); + } + + public JsonUnsopportedObjectException(Throwable cause) { + super(cause); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/tests/JsonParserTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/tests/JsonParserTest.java new file mode 100644 index 000000000..95935ebd6 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/temp/tests/JsonParserTest.java @@ -0,0 +1,419 @@ +package ru.fizteh.fivt.students.LebedevAleksey.temp.tests; + +import org.junit.Assert; +import org.junit.Test; +import ru.fizteh.fivt.students.LebedevAleksey.temp.json.BrokenJsonException; +import ru.fizteh.fivt.students.LebedevAleksey.temp.json.JsonParser; +import ru.fizteh.fivt.students.LebedevAleksey.temp.json.JsonUnsopportedObjectException; + +import java.io.File; +import java.util.*; + +public class JsonParserTest { + @Test + public void testGetJsonForStrings() throws JsonUnsopportedObjectException { + Assert.assertEquals("\"Qwerty\"", JsonParser.getJson("Qwerty")); + Assert.assertEquals("\"Test text\"", JsonParser.getJson("Test text")); + Assert.assertEquals("\"Text, fore test. Asdf!\"", JsonParser.getJson("Text, fore test. Asdf!")); + } + + @Test + public void testGetJsonForStringsWithCaracters() throws JsonUnsopportedObjectException { + Assert.assertEquals("\"Qwe\\nrty\"", JsonParser.getJson("Qwe\nrty")); + Assert.assertEquals("\"Qwe\\re r\\ty\"", JsonParser.getJson("Qwe\re r\ty")); + Assert.assertEquals("\"This is quote: \\\"test\\\".\"", JsonParser.getJson("This is quote: \"test\".")); + Assert.assertEquals("\"\\b\"", JsonParser.getJson("\b")); + Assert.assertEquals("\"\\f\"", JsonParser.getJson("\f")); + } + + @Test(expected = JsonUnsopportedObjectException.class) + public void testThrowExceptionForUnknownTypes() throws JsonUnsopportedObjectException { + JsonParser.getJson(new File(".")); + } + + + @Test() + public void testCanSerialiseIntegers() throws JsonUnsopportedObjectException { + Assert.assertEquals("1234", JsonParser.getJson(1234)); + Assert.assertEquals("123456789012345", JsonParser.getJson(123456789012345L)); + Assert.assertEquals("-4567", JsonParser.getJson(-4567)); + Assert.assertEquals("-923456789012345", JsonParser.getJson(-923456789012345L)); + Assert.assertEquals("123.45", JsonParser.getJson(123.45)); + Assert.assertEquals("123.45", JsonParser.getJson((float) 123.45)); + Assert.assertEquals("-123.45", JsonParser.getJson(-123.45)); + Assert.assertEquals("-123.45", JsonParser.getJson((float) -123.45)); + Assert.assertEquals("123.45", JsonParser.getJson((double) 123.45)); + Assert.assertEquals("-123.45", JsonParser.getJson((double) -123.45)); + Assert.assertEquals("123", JsonParser.getJson((byte) 123)); + Assert.assertEquals("-123", JsonParser.getJson((byte) -123)); + } + + @Test() + public void testCanSerialiseBools() throws JsonUnsopportedObjectException { + Assert.assertEquals("true", JsonParser.getJson(true)); + Assert.assertEquals("false", JsonParser.getJson(false)); + } + + @Test() + public void testCanSerialiseNull() throws JsonUnsopportedObjectException { + Assert.assertEquals("null", JsonParser.getJson(null)); + } + + @Test + public void testCanGetJsonForArrays() throws JsonUnsopportedObjectException { + Assert.assertEquals("[123,\"QwertY\"]", JsonParser.getJson(Arrays.asList(new Object[]{123, "QwertY"}))); + Assert.assertEquals("[123,\"QwertY\",true]", JsonParser.getJson(new Object[]{123, "QwertY", true})); + } + + + @Test + public void testCanGetJsonForMap() throws JsonUnsopportedObjectException { + checkMap(new TreeMap<>()); + checkMap(new HashMap<>()); + } + + private void checkMap(Map map) throws JsonUnsopportedObjectException { + map.put("b", true); + map.put("c", null); + map.put("dd", 3.1415); + String bString = "\"b\":true"; + String cString = "\"c\":null"; + String dString = "\"dd\":3.1415"; + String[] strings = new String[]{bString, cString, dString}; + int equalsCount = 0; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[2] = cString; + strings[1] = dString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[0] = cString; + strings[2] = bString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[2] = dString; + strings[1] = bString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[0] = dString; + strings[2] = cString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[1] = cString; + strings[2] = bString; + equalsCount = assertMapJson(map, strings, equalsCount); + Assert.assertEquals(1, equalsCount); + } + + private int assertMapJson(Map map, String[] strings, int equalsCount) + throws JsonUnsopportedObjectException { + if (("{" + strings[0] + "," + strings[1] + "," + strings[2] + "}").equals(JsonParser.getJson(map))) { + ++equalsCount; + } + return equalsCount; + } + + @Test + public void testSubItemJson() throws JsonUnsopportedObjectException { + Map map = new TreeMap<>(); + map.put("a", 1); + Object[] array = new Object[]{1, map, new Object[]{1.2, new Object[]{true}, null}}; + Assert.assertEquals("[1,{\"a\":1},[1.2,[true],null]]", JsonParser.getJson(array)); + } + + @Test + public void testCanJsonSupportedArrays() throws JsonUnsopportedObjectException { + Integer[] array = new Integer[]{1, 2, 3}; + Assert.assertEquals("[1,2,3]", (String) JsonParser.getJson(array)); + } + + + @Test + public void testParseStringJson() throws BrokenJsonException { + Assert.assertEquals("qwertY", JsonParser.parseJson("\"qwertY\"")); + Assert.assertEquals("qwe rtY", JsonParser.parseJson("\"qwe rtY\"")); + Assert.assertEquals("qwe\"rtY", JsonParser.parseJson("\"qwe\\\"rtY\"")); + Assert.assertEquals("\b\n\f\t\r\\/", JsonParser.parseJson("\"\\b\\n\\f\\t\\r\\\\\\/\"")); + Assert.assertEquals("Quick brown fox jump other the lazy dog.", + JsonParser.parseJson("\"Quick brown fox jump other the lazy dog.\"")); + Assert.assertEquals("\u0077cd", JsonParser.parseJson("\"\\u0077cd\"")); + Assert.assertEquals("\u9abc", JsonParser.parseJson("\"\\u9abc\"")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson1() throws BrokenJsonException { + JsonParser.parseJson("\"\\u\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson2() throws BrokenJsonException { + JsonParser.parseJson("\"\\u1\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson3() throws BrokenJsonException { + JsonParser.parseJson("\"\\u12\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson4() throws BrokenJsonException { + JsonParser.parseJson("\"\\u123\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson5() throws BrokenJsonException { + JsonParser.parseJson("\"\\u12x3\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson6() throws BrokenJsonException { + JsonParser.parseJson("\"aba"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson7() throws BrokenJsonException { + JsonParser.parseJson("\"a\\hba\""); + } + + @Test + public void testIntParse() throws BrokenJsonException { + JsonParser.setNeedShortIntegerTypes(false); + Object value = JsonParser.parseJson("123"); + Assert.assertEquals(Long.class, value.getClass()); + Assert.assertEquals(123L, value); + JsonParser.setNeedShortIntegerTypes(true); + value = JsonParser.parseJson("123"); + Assert.assertEquals(Byte.class, value.getClass()); + Assert.assertEquals((byte) 123, value); + value = JsonParser.parseJson("1234"); + Assert.assertEquals(Integer.class, value.getClass()); + Assert.assertEquals(1234, value); + JsonParser.setNeedShortIntegerTypes(false); + value = JsonParser.parseJson("4565678"); + Assert.assertEquals(4565678L, value); + value = JsonParser.parseJson("-4565678"); + Assert.assertEquals(-4565678L, value); + value = JsonParser.parseJson("-456.5678"); + Assert.assertEquals(-456.5678, value); + value = JsonParser.parseJson("123.45"); + Assert.assertEquals(123.45, value); + value = JsonParser.parseJson("1.23E5"); + Assert.assertEquals(1.23E5, value); + value = JsonParser.parseJson("-1.23E5"); + Assert.assertEquals(-1.23E5, value); + value = JsonParser.parseJson("1.23E-5"); + Assert.assertEquals(1.23E-5, value); + value = JsonParser.parseJson("-1.23E-5"); + Assert.assertEquals(-1.23E-5, value); + value = JsonParser.parseJson("1.23e5"); + Assert.assertEquals(1.23E5, value); + value = JsonParser.parseJson("-1.23e5"); + Assert.assertEquals(-1.23E5, value); + value = JsonParser.parseJson("1.23e-5"); + Assert.assertEquals(1.23E-5, value); + value = JsonParser.parseJson("-1.23e-5"); + Assert.assertEquals(-1.23E-5, value); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenDouble0() throws BrokenJsonException { + JsonParser.parseJson("12.34.56"); + } + + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenDouble1() throws BrokenJsonException { + JsonParser.parseJson("12.34E5e6"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenDouble2() throws BrokenJsonException { + JsonParser.parseJson("-12.34-56"); + } + + @Test + public void testParseNull() throws BrokenJsonException { + Assert.assertEquals(null, JsonParser.parseJson("null")); + } + + @Test + public void testParseTrue() throws BrokenJsonException { + Assert.assertEquals(true, JsonParser.parseJson("true")); + } + + @Test + public void testParseFalse() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("false")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseNullWithMistakes() throws BrokenJsonException { + Assert.assertEquals(null, JsonParser.parseJson("nul")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseTrueWithMistakes() throws BrokenJsonException { + Assert.assertEquals(true, JsonParser.parseJson("tre")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakes() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("fals")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakes2() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("fal")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakes3() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("falsq")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseNullWithMistakesAdditionalText() throws BrokenJsonException { + Assert.assertEquals(null, JsonParser.parseJson("nuller")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseTrueWithMistakesAdditionalText() throws BrokenJsonException { + Assert.assertEquals(true, JsonParser.parseJson("trueer")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakesAdditionalText() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("falseer")); + } + + @Test() + public void testCanParseArraysAndSpacesOk() throws BrokenJsonException { + checkArray(JsonParser.parseJson("[123.45,567,null,true,\"qwerty\"]")); + checkArray(JsonParser.parseJson(" [123.45, 567, null,true,\"qwerty\"]")); + checkArray(JsonParser.parseJson(" [ 123.45 , 567, null, true, \"qwerty\" ]")); + checkArray(JsonParser.parseJson("[ 123.45 , 567 , null , true , \"qwerty\" ] ")); + } + + private void checkArray(Object result) { + List array = (List) result; + Assert.assertEquals(5, array.size()); + Assert.assertEquals(123.45, array.get(0)); + Assert.assertEquals(567L, array.get(1)); + Assert.assertEquals(null, array.get(2)); + Assert.assertEquals(true, array.get(3)); + Assert.assertEquals("qwerty", array.get(4)); + } + + + @Test() + public void testCanParseMaps() throws BrokenJsonException { + checkMap(JsonParser.parseJson( + "{\"a\":1,\"b\":-2.3e2,\"c\":null,\"def\":true,\"false\":false,\"E\":\"Text \\\"with\\n...\"}")); + checkMap(JsonParser.parseJson( + " { \"a\":1,\"b\":-2.3e2 , \"c\":null,\"def\":true, \"false\":false, \"E\"" + + ":\"Text \\\"with\\n...\" } ")); + checkMap(JsonParser.parseJson( + " {\"a\":1,\"b\":-2.3e2 ,\"c\":null,\"def\":true , \"false\":false,\"E\":\"Te" + + "xt \\\"with\\n...\" }")); + checkMap(JsonParser.parseJson( + "{ \"a\":1,\"b\":-2.3e2,\"c\":null,\"def\":true,\"false\":false,\"E\":\"Text \\\"with\\n...\"} ")); + } + + private void checkMap(Object result) { + Map map = (Map) result; + Assert.assertEquals(6, map.size()); + Assert.assertEquals(1L, map.get("a")); + Assert.assertEquals(-230.0, map.get("b")); + Assert.assertEquals(null, map.get("c")); + Assert.assertEquals(true, map.get("def")); + Assert.assertEquals(false, map.get("false")); + Assert.assertEquals("Text \"with\n...", map.get("E")); + } + + @Test() + public void testCanParseMultiObjects() throws BrokenJsonException { + checkMultiArray(JsonParser.parseJson("[\"qw\",{\"a\":\"b\"},[null,12.3,{}],[]]")); + checkMultiArray(JsonParser.parseJson("[\"qw\",{\"a\" : \"b\"},[null,12.3,{ } ],[ ]]")); + checkMultiArray(JsonParser.parseJson("[\"qw\" ,{\"a\" : \"b\" } , [null,12.3,{}], [ ] ] ")); + checkMultiArray(JsonParser.parseJson("[\"qw\",{\"a\":\"b\"},[null ,12.3, {} ], [] ]")); + } + + private void checkMultiArray(Object result) { + List array = (List) result; + Assert.assertEquals(4, array.size()); + Assert.assertEquals("qw", array.get(0)); + Map map = (Map) array.get(1); + Assert.assertEquals(1, map.size()); + Assert.assertEquals("b", map.get("a")); + List subList = (List) array.get(2); + Assert.assertEquals(3, subList.size()); + Assert.assertEquals(null, subList.get(0)); + Assert.assertEquals(12.3, subList.get(1)); + Assert.assertEquals(0, ((Map) subList.get(2)).size()); + Assert.assertEquals(0, ((List) array.get(3)).size()); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson0() throws BrokenJsonException { + JsonParser.parseJson("[ "); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson1() throws BrokenJsonException { + JsonParser.parseJson("["); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson2() throws BrokenJsonException { + JsonParser.parseJson("]"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson3() throws BrokenJsonException { + JsonParser.parseJson("}"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson4() throws BrokenJsonException { + JsonParser.parseJson("["); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson5() throws BrokenJsonException { + JsonParser.parseJson("{"); + } + + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson6() throws BrokenJsonException { + JsonParser.parseJson("\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson7() throws BrokenJsonException { + JsonParser.parseJson(""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson8() throws BrokenJsonException { + JsonParser.parseJson(" "); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson9() throws BrokenJsonException { + JsonParser.parseJson("nula"); + } + + @Test(expected = BrokenJsonException.class) + public void testWrongMapKey() throws BrokenJsonException { + JsonParser.parseJson("{1:1}"); + } + + @Test(expected = BrokenJsonException.class) + public void testNotTerminatedString() throws BrokenJsonException { + JsonParser.parseJson("\"qwerty"); + } + + + @Test(expected = JsonUnsopportedObjectException.class) + public void testWrongMapSerialise() throws JsonUnsopportedObjectException { + Map map = new TreeMap<>(); + map.put(1, 2); + JsonParser.getJson(map); + } +} From 62b421de880c6480e1e881f691ecbe3fe3267e8d Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 24 Nov 2014 17:40:27 +0300 Subject: [PATCH 04/36] Developing storeable --- .../LebedevAleksey/storeable/Database.java | 29 +++++++-------- .../LebedevAleksey/storeable/Storeable.java | 35 +++++++++++++------ .../storeable/StoreableTable.java | 4 +-- 3 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index 9716a785f..f4eb3703b 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -24,19 +24,19 @@ public class Database implements TableProvider { private static Map, String> typesStringMap = new TreeMap<>(); static { - stringTypesMap.put("int", int.class); - stringTypesMap.put("long", long.class); - stringTypesMap.put("byte", byte.class); - stringTypesMap.put("float", float.class); - stringTypesMap.put("double", double.class); - stringTypesMap.put("boolean", boolean.class); + stringTypesMap.put("int", Integer.class); + stringTypesMap.put("long", Long.class); + stringTypesMap.put("byte", Byte.class); + stringTypesMap.put("float", Float.class); + stringTypesMap.put("double", Double.class); + stringTypesMap.put("boolean", Boolean.class); stringTypesMap.put("String", String.class); - typesStringMap.put(int.class, "int"); - typesStringMap.put(long.class, "long"); - typesStringMap.put(byte.class, "byte"); - typesStringMap.put(float.class, "float"); - typesStringMap.put(double.class, "double"); - typesStringMap.put(boolean.class, "boolean"); + typesStringMap.put(Integer.class, "int"); + typesStringMap.put(Long.class, "long"); + typesStringMap.put(Byte.class, "byte"); + typesStringMap.put(Float.class, "float"); + typesStringMap.put(java.lang.Double.class, "double"); + typesStringMap.put(Boolean.class, "boolean"); typesStringMap.put(String.class, "String"); } @@ -116,12 +116,12 @@ private String createTableSignature(List> columnTypes) { return tableSignature; } - private Path getRootDirectoryPath() { + public Path getRootDirectoryPath() { return directoryPath; } private StoreableTable generateTable(String name) { - return null;//TODO + return new StoreableTable(name, this); } @Override @@ -134,6 +134,7 @@ public void removeTable(String name) throws IOException { @Override public Storeable deserialize(Table table, String value) throws ParseException { + //TODO throw new NotImplementedException(); return null; diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java index 09e64cdd8..d291208b1 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java @@ -3,56 +3,69 @@ import ru.fizteh.fivt.storage.structured.ColumnFormatException; public class Storeable implements ru.fizteh.fivt.storage.structured.Storeable { + public static final String INCORRECT_TYPE_MESSAGE = "Value type isn't correct."; private Object[] data; - private StoreableTable formatTable; + private StoreableTable table; public Storeable(Object[] data, StoreableTable table) { this.data = data; - this.formatTable = table; + this.table = table; } @Override public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { - + if (table.getColumnType(columnIndex) == value.getClass()) { + data[columnIndex] = value; + } else { + throw new ColumnFormatException(INCORRECT_TYPE_MESSAGE); + } } @Override public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { - return null; + return data[columnIndex]; } @Override public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return null; + return (Integer) assertColumnType(columnIndex, int.class); + } + + private Object assertColumnType(int columnIndex, Class type) { + if (table.getColumnType(columnIndex) == type) { + return data[columnIndex]; + } else { + throw new ColumnFormatException("This column type is not " + type.toString()); + } } @Override public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return null; + return (Long) assertColumnType(columnIndex, long.class); } @Override public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return null; + return (byte) assertColumnType(columnIndex, byte.class); } @Override public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return null; + return (float) assertColumnType(columnIndex, float.class); } @Override public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return null; + return (double) assertColumnType(columnIndex, double.class); } @Override public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return null; + return (boolean) assertColumnType(columnIndex, boolean.class); } @Override public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return null; + return (String) assertColumnType(columnIndex, String.class); } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java index 00e3ce094..c55454c27 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java @@ -15,8 +15,8 @@ public class StoreableTable implements ru.fizteh.fivt.storage.structured.Table { private Table stringTable; - public StoreableTable(String name, ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Database databaseParent) { - super(name, databaseParent); + public StoreableTable(String name, Database databaseParent) { + Table stringTable=new Table(name, databaseParent.getRootDirectoryPath()); } @Override From c23bddd621b072f67544db3012ffa24f9df298c5 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 24 Nov 2014 17:41:22 +0300 Subject: [PATCH 05/36] Move JSON to Storeable folder --- .../{temp => storeable}/json/BrokenJsonException.java | 2 +- .../{temp => storeable}/json/JsonParser.java | 2 +- .../json/JsonUnsopportedObjectException.java | 2 +- .../{temp => storeable}/tests/JsonParserTest.java | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) rename src/ru/fizteh/fivt/students/LebedevAleksey/{temp => storeable}/json/BrokenJsonException.java (85%) rename src/ru/fizteh/fivt/students/LebedevAleksey/{temp => storeable}/json/JsonParser.java (99%) rename src/ru/fizteh/fivt/students/LebedevAleksey/{temp => storeable}/json/JsonUnsopportedObjectException.java (86%) rename src/ru/fizteh/fivt/students/LebedevAleksey/{temp => storeable}/tests/JsonParserTest.java (98%) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/BrokenJsonException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java similarity index 85% rename from src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/BrokenJsonException.java rename to src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java index 62d2ff930..edf28e637 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/BrokenJsonException.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.LebedevAleksey.temp.json; +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; public class BrokenJsonException extends Exception { public BrokenJsonException() { diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java similarity index 99% rename from src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonParser.java rename to src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java index 7f3f23cf9..2d9908b33 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonParser.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.LebedevAleksey.temp.json; +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Pair; diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonUnsopportedObjectException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java similarity index 86% rename from src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonUnsopportedObjectException.java rename to src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java index dfb8e168d..dc65bc239 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/json/JsonUnsopportedObjectException.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java @@ -1,4 +1,4 @@ -package ru.fizteh.fivt.students.LebedevAleksey.temp.json; +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; public class JsonUnsopportedObjectException extends Exception { public JsonUnsopportedObjectException() { diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/tests/JsonParserTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java similarity index 98% rename from src/ru/fizteh/fivt/students/LebedevAleksey/temp/tests/JsonParserTest.java rename to src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java index 95935ebd6..ea233ba33 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/temp/tests/JsonParserTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java @@ -1,10 +1,10 @@ -package ru.fizteh.fivt.students.LebedevAleksey.temp.tests; +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; import org.junit.Assert; import org.junit.Test; -import ru.fizteh.fivt.students.LebedevAleksey.temp.json.BrokenJsonException; -import ru.fizteh.fivt.students.LebedevAleksey.temp.json.JsonParser; -import ru.fizteh.fivt.students.LebedevAleksey.temp.json.JsonUnsopportedObjectException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsopportedObjectException; import java.io.File; import java.util.*; From 417b4a526a42d42a50cd5e715e87df772c8fa121 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 24 Nov 2014 17:41:22 +0300 Subject: [PATCH 06/36] Add JSON --- .../storeable/json/BrokenJsonException.java | 18 + .../storeable/json/JsonParser.java | 413 +++++++++++++++++ .../json/JsonUnsopportedObjectException.java | 18 + .../storeable/tests/JsonParserTest.java | 419 ++++++++++++++++++ 4 files changed, 868 insertions(+) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java new file mode 100644 index 000000000..edf28e637 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; + +public class BrokenJsonException extends Exception { + public BrokenJsonException() { + } + + public BrokenJsonException(String message) { + super(message); + } + + public BrokenJsonException(String message, Throwable cause) { + super(message, cause); + } + + public BrokenJsonException(Throwable cause) { + super(cause); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java new file mode 100644 index 000000000..2d9908b33 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -0,0 +1,413 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; + +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Pair; + +import java.util.*; + +public abstract class JsonParser { + public static final String UNEXPECTED_STOP_MESSAGE = "Parsers didn't reach the end."; + private static final ThreadLocal NEED_SHORT_INTEGER_TYPES = new ThreadLocal() { + @Override + protected Boolean initialValue() { + return false; + } + }; + + public static void setNeedShortIntegerTypes(boolean needShortIntegerTypes) { + JsonParser.NEED_SHORT_INTEGER_TYPES.set(needShortIntegerTypes); + } + + public static String getJson(Object data) throws JsonUnsopportedObjectException { + StringBuilder result = new StringBuilder(); + createJson(data, result); + return result.toString(); + } + + public static Object parseJson(String json) throws BrokenJsonException { + json = json.trim(); + Pair result = null; + try { + result = parseJson(json, 0, null); + } catch (JsonTerminatedException e) { + throw new BrokenJsonException("Wrong JSON"); + } + if (result.getValue() != json.length()) { + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + return result.getKey(); + } + + public static Pair parseJson(String json, int begin, Character possibleTerminator) + throws BrokenJsonException, JsonTerminatedException { + Character first = null; + List list; + Map map; + int i; + for (i = begin; i < json.length(); i++) { + char symbol = json.charAt(i); + if (possibleTerminator != null && possibleTerminator.equals(symbol)) { + throw new JsonTerminatedException(); + } + switch (symbol) { + case ' ': + break; + case '"': + return parseStringFromJson(json, i + 1); + case '{': + return parseMapFromJson(json, i + 1); + case '[': + return parseArrayFromJson(json, i + 1); + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parseNumberFromJson(json, i); + case 'n': + case 't': + case 'f': + try { + switch (json.substring(i, i + 4)) { + case "null": + return new Pair<>(null, i + 4); + case "true": + return new Pair<>(true, i + 4); + case "fals": + if ("false".equals(json.substring(i, i + 5))) { + return new Pair<>(false, i + 5); + } else { + throw new BrokenJsonException("Unexpected symbol in position " + i + 4); + } + default: + throw new BrokenJsonException("Wrong token"); + } + } catch (IndexOutOfBoundsException e) { + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, e); + } + default: + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i); + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static Pair parseMapFromJson(String json, int begin) throws BrokenJsonException { + Map map = new TreeMap<>(); + int i = begin; + while (i < json.length()) { + Pair result = null; + try { + result = parseJson(json, i, '}'); + } catch (JsonTerminatedException e) { + return searchNextChar(json, map, i, '}'); + } + String key; + try { + key = (String) result.getKey(); + } catch (ClassCastException e) { + throw new BrokenJsonException("Wrong map key"); + } + i = result.getValue(); + boolean exited = false; + for (; i < json.length() && (!exited); ++i) { + char symbol = json.charAt(i); + switch (symbol) { + case ' ': + break; + case ':': + exited = true; + break; + default: + break; + } + } + try { + result = parseJson(json, i, null); + } catch (JsonTerminatedException e) { + throw new BrokenJsonException("JSON is not correct"); + } + map.put(key, result.getKey()); + i = result.getValue(); + exited = false; + for (; i < json.length() && (!exited); ++i) { + char symbol = json.charAt(i); + switch (symbol) { + case ' ': + break; + case ',': + exited = true; + break; + case '}': + return new Pair<>(map, i + 1); + default: + break; + } + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static Pair searchNextChar(String json, Object value, int i, char mask) { + while (json.charAt(i) != mask) { + ++i; + } + return new Pair<>(value, i + 1); + } + + + private static Pair parseArrayFromJson(String json, int begin) throws BrokenJsonException { + List list = new ArrayList<>(); + int i = begin; + while (i < json.length()) { + Pair result = null; + try { + result = parseJson(json, i, ']'); + } catch (JsonTerminatedException e) { + return searchNextChar(json, list, i, ']'); + } + list.add(result.getKey()); + i = result.getValue(); + boolean exited = false; + for (; i < json.length() && (!exited); ++i) { + char symbol = json.charAt(i); + switch (symbol) { + case ' ': + break; + case ',': + exited = true; + break; + case ']': + return new Pair<>(list, i + 1); + default: + break; + } + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static Pair parseNumberFromJson(String json, int begin) throws BrokenJsonException { + List posibleCharcters = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + posibleCharcters.add((char) ('0' + i)); + } + posibleCharcters.add('+'); + posibleCharcters.add('-'); + posibleCharcters.add('E'); + posibleCharcters.add('e'); + posibleCharcters.add('.'); + int i; + for (i = begin; i < json.length(); i++) { + char symbol = json.charAt(i); + if (!posibleCharcters.contains(symbol)) { + break; + } + } + String number = json.substring(begin, i); + if (NEED_SHORT_INTEGER_TYPES.get()) { + try { + byte byteValue = Byte.parseByte(number); + return new Pair<>(byteValue, i); + } catch (NumberFormatException e) { + // Next type... + } + try { + int intValue = Integer.parseInt(number); + return new Pair<>(intValue, i); + } catch (NumberFormatException e) { + // Next type... + } + } + try { + long longValue = Long.parseLong(number); + return new Pair<>(longValue, i); + } catch (NumberFormatException e) { + // Next type... + } + try { + double doubleValue = Double.parseDouble(number); + return new Pair<>(doubleValue, i); + } catch (NumberFormatException e) { + // Next type... + } + throw new BrokenJsonException("Wrong number"); + } + + private static Pair parseStringFromJson(String json, int begin) + throws BrokenJsonException { + StringBuilder builder = new StringBuilder(); + for (int i = begin; i < json.length(); i++) { + char symbol = json.charAt(i); + if (symbol == '\\') { + i = parseSlashSequense(json, builder, i); + } else { + if (symbol == '"') { + return new Pair<>(builder.toString(), i + 1); + } else { + builder.append(symbol); + } + } + } + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + } + + private static int parseSlashSequense(String json, StringBuilder builder, int i) throws BrokenJsonException { + if (i + 1 == json.length()) { + throw new BrokenJsonException("String doesn't finish"); + } + switch (json.charAt(i + 1)) { + case '"': + builder.append("\""); + return i + 1; + case '\\': + builder.append("\\"); + return i + 1; + case '/': + builder.append("/"); + return i + 1; + case 'b': + builder.append("\b"); + return i + 1; + case 'f': + builder.append("\f"); + return i + 1; + case 'n': + builder.append("\n"); + return i + 1; + case 'r': + builder.append("\r"); + return i + 1; + case 't': + builder.append("\t"); + return i + 1; + case 'u': + if (i + 5 >= json.length()) { + throw new BrokenJsonException("Error in \\u in string"); + } + try { + int num = Integer.parseInt(json.substring(i + 2, i + 6), 16); + builder.append((char) num); + return i + 5; + } catch (NumberFormatException e) { + throw new BrokenJsonException("Error in \\u in string: can't parse int", e); + } + default: + throw new BrokenJsonException("Unknown \\ sequence in char number " + i); + } + } + + private static void createJson(Object data, StringBuilder builder) throws JsonUnsopportedObjectException { + if (data == null) { + builder.append("null"); + return; + } + if (containsInterface(data, Map.class)) { + Map map; + try { + map = (Map) data; + } catch (ClassCastException e) { + throw new JsonUnsopportedObjectException("Only Map is supported, when you use map", e); + } + createJson(map, builder); + return; + } + if (containsInterface(data, Iterable.class)) { + createJson((Iterable) data, builder); + return; + } + if (containsInterface(data, Object[].class)) { + createJson((Object[]) data, builder); + return; + } + if (data.getClass() == String.class) { + createJson((String) data, builder); + return; + } + if (data.getClass().getSuperclass() == Number.class) { + builder.append(data.toString()); + return; + } + if (data.getClass() == Boolean.class) { + builder.append(((boolean) data) ? "true" : false); + return; + } + throw new JsonUnsopportedObjectException("This type (" + data.getClass() + ") is unsupported by JSON."); + } + + private static void createJson(String data, StringBuilder builder) { + data = data.replace("\\", "\\\\").replace("\"", "\\\""); + data = data.replace("/", "\\/").replace("\b", "\\b"); + data = data.replace("\f", "\\f").replace("\n", "\\n"); + data = data.replace("\r", "\\r").replace("\t", "\\t"); + builder.append("\""); + builder.append(data); + builder.append("\""); + } + + private static boolean containsInterface(Object data, Class type) { + try { + type.cast(data); + return true; + } catch (ClassCastException e) { + return false; + } + } + + private static void createJson(Iterable data, StringBuilder builder) throws JsonUnsopportedObjectException { + builder.append("["); + boolean first = true; + for (Object item : data) { + if (!first) { + builder.append(","); + } else { + first = false; + } + createJson(item, builder); + } + builder.append("]"); + } + + private static void createJson(Object[] data, StringBuilder builder) throws JsonUnsopportedObjectException { + builder.append("["); + boolean first = true; + for (Object item : data) { + if (!first) { + builder.append(","); + } else { + first = false; + } + createJson(item, builder); + } + builder.append("]"); + } + + private static void createJson(Map data, StringBuilder builder) throws JsonUnsopportedObjectException { + try { + builder.append("{"); + Set keys = data.keySet(); + boolean notFirst = false; + for (String item : keys) { + if (notFirst) { + builder.append(","); + } else { + notFirst = true; + } + createJson(item, builder); + builder.append(":"); + createJson(data.get(item), builder); + } + builder.append("}"); + } catch (ClassCastException e) { + throw new JsonUnsopportedObjectException("Only Map is supported.", e); + } + } +} + +class JsonTerminatedException extends Exception { +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java new file mode 100644 index 000000000..dc65bc239 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; + +public class JsonUnsopportedObjectException extends Exception { + public JsonUnsopportedObjectException() { + } + + public JsonUnsopportedObjectException(String message) { + super(message); + } + + public JsonUnsopportedObjectException(String message, Throwable cause) { + super(message, cause); + } + + public JsonUnsopportedObjectException(Throwable cause) { + super(cause); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java new file mode 100644 index 000000000..ea233ba33 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java @@ -0,0 +1,419 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.Assert; +import org.junit.Test; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsopportedObjectException; + +import java.io.File; +import java.util.*; + +public class JsonParserTest { + @Test + public void testGetJsonForStrings() throws JsonUnsopportedObjectException { + Assert.assertEquals("\"Qwerty\"", JsonParser.getJson("Qwerty")); + Assert.assertEquals("\"Test text\"", JsonParser.getJson("Test text")); + Assert.assertEquals("\"Text, fore test. Asdf!\"", JsonParser.getJson("Text, fore test. Asdf!")); + } + + @Test + public void testGetJsonForStringsWithCaracters() throws JsonUnsopportedObjectException { + Assert.assertEquals("\"Qwe\\nrty\"", JsonParser.getJson("Qwe\nrty")); + Assert.assertEquals("\"Qwe\\re r\\ty\"", JsonParser.getJson("Qwe\re r\ty")); + Assert.assertEquals("\"This is quote: \\\"test\\\".\"", JsonParser.getJson("This is quote: \"test\".")); + Assert.assertEquals("\"\\b\"", JsonParser.getJson("\b")); + Assert.assertEquals("\"\\f\"", JsonParser.getJson("\f")); + } + + @Test(expected = JsonUnsopportedObjectException.class) + public void testThrowExceptionForUnknownTypes() throws JsonUnsopportedObjectException { + JsonParser.getJson(new File(".")); + } + + + @Test() + public void testCanSerialiseIntegers() throws JsonUnsopportedObjectException { + Assert.assertEquals("1234", JsonParser.getJson(1234)); + Assert.assertEquals("123456789012345", JsonParser.getJson(123456789012345L)); + Assert.assertEquals("-4567", JsonParser.getJson(-4567)); + Assert.assertEquals("-923456789012345", JsonParser.getJson(-923456789012345L)); + Assert.assertEquals("123.45", JsonParser.getJson(123.45)); + Assert.assertEquals("123.45", JsonParser.getJson((float) 123.45)); + Assert.assertEquals("-123.45", JsonParser.getJson(-123.45)); + Assert.assertEquals("-123.45", JsonParser.getJson((float) -123.45)); + Assert.assertEquals("123.45", JsonParser.getJson((double) 123.45)); + Assert.assertEquals("-123.45", JsonParser.getJson((double) -123.45)); + Assert.assertEquals("123", JsonParser.getJson((byte) 123)); + Assert.assertEquals("-123", JsonParser.getJson((byte) -123)); + } + + @Test() + public void testCanSerialiseBools() throws JsonUnsopportedObjectException { + Assert.assertEquals("true", JsonParser.getJson(true)); + Assert.assertEquals("false", JsonParser.getJson(false)); + } + + @Test() + public void testCanSerialiseNull() throws JsonUnsopportedObjectException { + Assert.assertEquals("null", JsonParser.getJson(null)); + } + + @Test + public void testCanGetJsonForArrays() throws JsonUnsopportedObjectException { + Assert.assertEquals("[123,\"QwertY\"]", JsonParser.getJson(Arrays.asList(new Object[]{123, "QwertY"}))); + Assert.assertEquals("[123,\"QwertY\",true]", JsonParser.getJson(new Object[]{123, "QwertY", true})); + } + + + @Test + public void testCanGetJsonForMap() throws JsonUnsopportedObjectException { + checkMap(new TreeMap<>()); + checkMap(new HashMap<>()); + } + + private void checkMap(Map map) throws JsonUnsopportedObjectException { + map.put("b", true); + map.put("c", null); + map.put("dd", 3.1415); + String bString = "\"b\":true"; + String cString = "\"c\":null"; + String dString = "\"dd\":3.1415"; + String[] strings = new String[]{bString, cString, dString}; + int equalsCount = 0; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[2] = cString; + strings[1] = dString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[0] = cString; + strings[2] = bString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[2] = dString; + strings[1] = bString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[0] = dString; + strings[2] = cString; + equalsCount = assertMapJson(map, strings, equalsCount); + strings[1] = cString; + strings[2] = bString; + equalsCount = assertMapJson(map, strings, equalsCount); + Assert.assertEquals(1, equalsCount); + } + + private int assertMapJson(Map map, String[] strings, int equalsCount) + throws JsonUnsopportedObjectException { + if (("{" + strings[0] + "," + strings[1] + "," + strings[2] + "}").equals(JsonParser.getJson(map))) { + ++equalsCount; + } + return equalsCount; + } + + @Test + public void testSubItemJson() throws JsonUnsopportedObjectException { + Map map = new TreeMap<>(); + map.put("a", 1); + Object[] array = new Object[]{1, map, new Object[]{1.2, new Object[]{true}, null}}; + Assert.assertEquals("[1,{\"a\":1},[1.2,[true],null]]", JsonParser.getJson(array)); + } + + @Test + public void testCanJsonSupportedArrays() throws JsonUnsopportedObjectException { + Integer[] array = new Integer[]{1, 2, 3}; + Assert.assertEquals("[1,2,3]", (String) JsonParser.getJson(array)); + } + + + @Test + public void testParseStringJson() throws BrokenJsonException { + Assert.assertEquals("qwertY", JsonParser.parseJson("\"qwertY\"")); + Assert.assertEquals("qwe rtY", JsonParser.parseJson("\"qwe rtY\"")); + Assert.assertEquals("qwe\"rtY", JsonParser.parseJson("\"qwe\\\"rtY\"")); + Assert.assertEquals("\b\n\f\t\r\\/", JsonParser.parseJson("\"\\b\\n\\f\\t\\r\\\\\\/\"")); + Assert.assertEquals("Quick brown fox jump other the lazy dog.", + JsonParser.parseJson("\"Quick brown fox jump other the lazy dog.\"")); + Assert.assertEquals("\u0077cd", JsonParser.parseJson("\"\\u0077cd\"")); + Assert.assertEquals("\u9abc", JsonParser.parseJson("\"\\u9abc\"")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson1() throws BrokenJsonException { + JsonParser.parseJson("\"\\u\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson2() throws BrokenJsonException { + JsonParser.parseJson("\"\\u1\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson3() throws BrokenJsonException { + JsonParser.parseJson("\"\\u12\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson4() throws BrokenJsonException { + JsonParser.parseJson("\"\\u123\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson5() throws BrokenJsonException { + JsonParser.parseJson("\"\\u12x3\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson6() throws BrokenJsonException { + JsonParser.parseJson("\"aba"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenStringJson7() throws BrokenJsonException { + JsonParser.parseJson("\"a\\hba\""); + } + + @Test + public void testIntParse() throws BrokenJsonException { + JsonParser.setNeedShortIntegerTypes(false); + Object value = JsonParser.parseJson("123"); + Assert.assertEquals(Long.class, value.getClass()); + Assert.assertEquals(123L, value); + JsonParser.setNeedShortIntegerTypes(true); + value = JsonParser.parseJson("123"); + Assert.assertEquals(Byte.class, value.getClass()); + Assert.assertEquals((byte) 123, value); + value = JsonParser.parseJson("1234"); + Assert.assertEquals(Integer.class, value.getClass()); + Assert.assertEquals(1234, value); + JsonParser.setNeedShortIntegerTypes(false); + value = JsonParser.parseJson("4565678"); + Assert.assertEquals(4565678L, value); + value = JsonParser.parseJson("-4565678"); + Assert.assertEquals(-4565678L, value); + value = JsonParser.parseJson("-456.5678"); + Assert.assertEquals(-456.5678, value); + value = JsonParser.parseJson("123.45"); + Assert.assertEquals(123.45, value); + value = JsonParser.parseJson("1.23E5"); + Assert.assertEquals(1.23E5, value); + value = JsonParser.parseJson("-1.23E5"); + Assert.assertEquals(-1.23E5, value); + value = JsonParser.parseJson("1.23E-5"); + Assert.assertEquals(1.23E-5, value); + value = JsonParser.parseJson("-1.23E-5"); + Assert.assertEquals(-1.23E-5, value); + value = JsonParser.parseJson("1.23e5"); + Assert.assertEquals(1.23E5, value); + value = JsonParser.parseJson("-1.23e5"); + Assert.assertEquals(-1.23E5, value); + value = JsonParser.parseJson("1.23e-5"); + Assert.assertEquals(1.23E-5, value); + value = JsonParser.parseJson("-1.23e-5"); + Assert.assertEquals(-1.23E-5, value); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenDouble0() throws BrokenJsonException { + JsonParser.parseJson("12.34.56"); + } + + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenDouble1() throws BrokenJsonException { + JsonParser.parseJson("12.34E5e6"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenDouble2() throws BrokenJsonException { + JsonParser.parseJson("-12.34-56"); + } + + @Test + public void testParseNull() throws BrokenJsonException { + Assert.assertEquals(null, JsonParser.parseJson("null")); + } + + @Test + public void testParseTrue() throws BrokenJsonException { + Assert.assertEquals(true, JsonParser.parseJson("true")); + } + + @Test + public void testParseFalse() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("false")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseNullWithMistakes() throws BrokenJsonException { + Assert.assertEquals(null, JsonParser.parseJson("nul")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseTrueWithMistakes() throws BrokenJsonException { + Assert.assertEquals(true, JsonParser.parseJson("tre")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakes() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("fals")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakes2() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("fal")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakes3() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("falsq")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseNullWithMistakesAdditionalText() throws BrokenJsonException { + Assert.assertEquals(null, JsonParser.parseJson("nuller")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseTrueWithMistakesAdditionalText() throws BrokenJsonException { + Assert.assertEquals(true, JsonParser.parseJson("trueer")); + } + + @Test(expected = BrokenJsonException.class) + public void testParseFalseWithMistakesAdditionalText() throws BrokenJsonException { + Assert.assertEquals(false, JsonParser.parseJson("falseer")); + } + + @Test() + public void testCanParseArraysAndSpacesOk() throws BrokenJsonException { + checkArray(JsonParser.parseJson("[123.45,567,null,true,\"qwerty\"]")); + checkArray(JsonParser.parseJson(" [123.45, 567, null,true,\"qwerty\"]")); + checkArray(JsonParser.parseJson(" [ 123.45 , 567, null, true, \"qwerty\" ]")); + checkArray(JsonParser.parseJson("[ 123.45 , 567 , null , true , \"qwerty\" ] ")); + } + + private void checkArray(Object result) { + List array = (List) result; + Assert.assertEquals(5, array.size()); + Assert.assertEquals(123.45, array.get(0)); + Assert.assertEquals(567L, array.get(1)); + Assert.assertEquals(null, array.get(2)); + Assert.assertEquals(true, array.get(3)); + Assert.assertEquals("qwerty", array.get(4)); + } + + + @Test() + public void testCanParseMaps() throws BrokenJsonException { + checkMap(JsonParser.parseJson( + "{\"a\":1,\"b\":-2.3e2,\"c\":null,\"def\":true,\"false\":false,\"E\":\"Text \\\"with\\n...\"}")); + checkMap(JsonParser.parseJson( + " { \"a\":1,\"b\":-2.3e2 , \"c\":null,\"def\":true, \"false\":false, \"E\"" + + ":\"Text \\\"with\\n...\" } ")); + checkMap(JsonParser.parseJson( + " {\"a\":1,\"b\":-2.3e2 ,\"c\":null,\"def\":true , \"false\":false,\"E\":\"Te" + + "xt \\\"with\\n...\" }")); + checkMap(JsonParser.parseJson( + "{ \"a\":1,\"b\":-2.3e2,\"c\":null,\"def\":true,\"false\":false,\"E\":\"Text \\\"with\\n...\"} ")); + } + + private void checkMap(Object result) { + Map map = (Map) result; + Assert.assertEquals(6, map.size()); + Assert.assertEquals(1L, map.get("a")); + Assert.assertEquals(-230.0, map.get("b")); + Assert.assertEquals(null, map.get("c")); + Assert.assertEquals(true, map.get("def")); + Assert.assertEquals(false, map.get("false")); + Assert.assertEquals("Text \"with\n...", map.get("E")); + } + + @Test() + public void testCanParseMultiObjects() throws BrokenJsonException { + checkMultiArray(JsonParser.parseJson("[\"qw\",{\"a\":\"b\"},[null,12.3,{}],[]]")); + checkMultiArray(JsonParser.parseJson("[\"qw\",{\"a\" : \"b\"},[null,12.3,{ } ],[ ]]")); + checkMultiArray(JsonParser.parseJson("[\"qw\" ,{\"a\" : \"b\" } , [null,12.3,{}], [ ] ] ")); + checkMultiArray(JsonParser.parseJson("[\"qw\",{\"a\":\"b\"},[null ,12.3, {} ], [] ]")); + } + + private void checkMultiArray(Object result) { + List array = (List) result; + Assert.assertEquals(4, array.size()); + Assert.assertEquals("qw", array.get(0)); + Map map = (Map) array.get(1); + Assert.assertEquals(1, map.size()); + Assert.assertEquals("b", map.get("a")); + List subList = (List) array.get(2); + Assert.assertEquals(3, subList.size()); + Assert.assertEquals(null, subList.get(0)); + Assert.assertEquals(12.3, subList.get(1)); + Assert.assertEquals(0, ((Map) subList.get(2)).size()); + Assert.assertEquals(0, ((List) array.get(3)).size()); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson0() throws BrokenJsonException { + JsonParser.parseJson("[ "); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson1() throws BrokenJsonException { + JsonParser.parseJson("["); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson2() throws BrokenJsonException { + JsonParser.parseJson("]"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson3() throws BrokenJsonException { + JsonParser.parseJson("}"); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson4() throws BrokenJsonException { + JsonParser.parseJson("["); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson5() throws BrokenJsonException { + JsonParser.parseJson("{"); + } + + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson6() throws BrokenJsonException { + JsonParser.parseJson("\""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson7() throws BrokenJsonException { + JsonParser.parseJson(""); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson8() throws BrokenJsonException { + JsonParser.parseJson(" "); + } + + @Test(expected = BrokenJsonException.class) + public void testParseBrokenJson9() throws BrokenJsonException { + JsonParser.parseJson("nula"); + } + + @Test(expected = BrokenJsonException.class) + public void testWrongMapKey() throws BrokenJsonException { + JsonParser.parseJson("{1:1}"); + } + + @Test(expected = BrokenJsonException.class) + public void testNotTerminatedString() throws BrokenJsonException { + JsonParser.parseJson("\"qwerty"); + } + + + @Test(expected = JsonUnsopportedObjectException.class) + public void testWrongMapSerialise() throws JsonUnsopportedObjectException { + Map map = new TreeMap<>(); + map.put(1, 2); + JsonParser.getJson(map); + } +} From 31d4a3ab0941bf2df13b7082ead74ef94e89f171 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 24 Nov 2014 20:46:50 +0300 Subject: [PATCH 07/36] Some cleanup --- .../students/LebedevAleksey/storeable/json/JsonParser.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java index 2d9908b33..d7a903ebb 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -39,9 +39,6 @@ public static Object parseJson(String json) throws BrokenJsonException { public static Pair parseJson(String json, int begin, Character possibleTerminator) throws BrokenJsonException, JsonTerminatedException { - Character first = null; - List list; - Map map; int i; for (i = begin; i < json.length(); i++) { char symbol = json.charAt(i); From 3042b880b146664d31d68bb6e0e41d667955cdda Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Fri, 28 Nov 2014 21:09:58 +0300 Subject: [PATCH 08/36] Something --- .../LebedevAleksey/storeable/Database.java | 5 ++--- .../LebedevAleksey/storeable/Storeable.java | 15 +++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index f4eb3703b..cfbb9348c 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -149,9 +149,8 @@ public String serialize(Table table, Storeable value) throws ColumnFormatExcepti @Override public Storeable createFor(Table table) { - //TODO - throw new NotImplementedException(); - return null; + return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable(new Object[table.getColumnsCount()], + table); } @Override diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java index d291208b1..9cb551524 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java @@ -1,13 +1,16 @@ package ru.fizteh.fivt.students.LebedevAleksey.storeable; import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Table; + +import java.util.List; public class Storeable implements ru.fizteh.fivt.storage.structured.Storeable { public static final String INCORRECT_TYPE_MESSAGE = "Value type isn't correct."; - private Object[] data; - private StoreableTable table; + private List data; + private Table table; - public Storeable(Object[] data, StoreableTable table) { + public Storeable(List data, Table table) { this.data = data; this.table = table; } @@ -15,7 +18,7 @@ public Storeable(Object[] data, StoreableTable table) { @Override public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { if (table.getColumnType(columnIndex) == value.getClass()) { - data[columnIndex] = value; + data.set(columnIndex, value); } else { throw new ColumnFormatException(INCORRECT_TYPE_MESSAGE); } @@ -23,7 +26,7 @@ public void setColumnAt(int columnIndex, Object value) throws ColumnFormatExcept @Override public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { - return data[columnIndex]; + return data.get(columnIndex); } @Override @@ -33,7 +36,7 @@ public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutO private Object assertColumnType(int columnIndex, Class type) { if (table.getColumnType(columnIndex) == type) { - return data[columnIndex]; + return data.get(columnIndex); } else { throw new ColumnFormatException("This column type is not " + type.toString()); } From e94304e1d387bd149e2edbeb2edba42e9b00b483 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sat, 29 Nov 2014 13:37:18 +0300 Subject: [PATCH 09/36] Add offset number to JSON --- .../storeable/json/BrokenJsonException.java | 13 ++--- .../storeable/json/JsonParser.java | 48 +++++++++++-------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java index edf28e637..b9f4a23f9 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java @@ -1,18 +1,19 @@ package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; public class BrokenJsonException extends Exception { - public BrokenJsonException() { - } + private int offsetError; - public BrokenJsonException(String message) { + public BrokenJsonException(String message, int offsetError) { super(message); + this.offsetError = offsetError; } - public BrokenJsonException(String message, Throwable cause) { + public BrokenJsonException(String message, Throwable cause, int offsetError) { super(message, cause); + this.offsetError = offsetError; } - public BrokenJsonException(Throwable cause) { - super(cause); + public int getOffsetError() { + return offsetError; } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java index 2d9908b33..8d2d1b42f 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -25,14 +25,14 @@ public static String getJson(Object data) throws JsonUnsopportedObjectException public static Object parseJson(String json) throws BrokenJsonException { json = json.trim(); - Pair result = null; + Pair result; try { result = parseJson(json, 0, null); } catch (JsonTerminatedException e) { - throw new BrokenJsonException("Wrong JSON"); + throw new BrokenJsonException("Wrong JSON", json.length() > 0 ? json.length() - 1 : 0); } if (result.getValue() != json.length()) { - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, result.getValue()); } return result.getKey(); } @@ -46,7 +46,7 @@ public static Pair parseJson(String json, int begin, Character for (i = begin; i < json.length(); i++) { char symbol = json.charAt(i); if (possibleTerminator != null && possibleTerminator.equals(symbol)) { - throw new JsonTerminatedException(); + throw new JsonTerminatedException(i); } switch (symbol) { case ' ': @@ -82,19 +82,19 @@ public static Pair parseJson(String json, int begin, Character if ("false".equals(json.substring(i, i + 5))) { return new Pair<>(false, i + 5); } else { - throw new BrokenJsonException("Unexpected symbol in position " + i + 4); + throw new BrokenJsonException("Unexpected symbol in position " + i + 4, i + 4); } default: - throw new BrokenJsonException("Wrong token"); + throw new BrokenJsonException("Wrong token", i); } } catch (IndexOutOfBoundsException e) { - throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, e); + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, e, i); } default: - throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i); + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, i); } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, i); } private static Pair parseMapFromJson(String json, int begin) throws BrokenJsonException { @@ -111,7 +111,7 @@ private static Pair parseMapFromJson(String json, int begin) th try { key = (String) result.getKey(); } catch (ClassCastException e) { - throw new BrokenJsonException("Wrong map key"); + throw new BrokenJsonException("Wrong map key", i); } i = result.getValue(); boolean exited = false; @@ -130,7 +130,7 @@ private static Pair parseMapFromJson(String json, int begin) th try { result = parseJson(json, i, null); } catch (JsonTerminatedException e) { - throw new BrokenJsonException("JSON is not correct"); + throw new BrokenJsonException("JSON is not correct", e.getOffset()); } map.put(key, result.getKey()); i = result.getValue(); @@ -150,7 +150,7 @@ private static Pair parseMapFromJson(String json, int begin) th } } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, i); } private static Pair searchNextChar(String json, Object value, int i, char mask) { @@ -189,7 +189,7 @@ private static Pair parseArrayFromJson(String json, int begin) } } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, i); } private static Pair parseNumberFromJson(String json, int begin) throws BrokenJsonException { @@ -236,7 +236,7 @@ private static Pair parseNumberFromJson(String json, int begin) } catch (NumberFormatException e) { // Next type... } - throw new BrokenJsonException("Wrong number"); + throw new BrokenJsonException("Wrong number", i); } private static Pair parseStringFromJson(String json, int begin) @@ -254,12 +254,12 @@ private static Pair parseStringFromJson(String json, int begin) } } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, json.length() - 1); } private static int parseSlashSequense(String json, StringBuilder builder, int i) throws BrokenJsonException { if (i + 1 == json.length()) { - throw new BrokenJsonException("String doesn't finish"); + throw new BrokenJsonException("String doesn't finish", i); } switch (json.charAt(i + 1)) { case '"': @@ -288,17 +288,17 @@ private static int parseSlashSequense(String json, StringBuilder builder, int i) return i + 1; case 'u': if (i + 5 >= json.length()) { - throw new BrokenJsonException("Error in \\u in string"); + throw new BrokenJsonException("Error in \\u in string", i); } try { int num = Integer.parseInt(json.substring(i + 2, i + 6), 16); builder.append((char) num); return i + 5; } catch (NumberFormatException e) { - throw new BrokenJsonException("Error in \\u in string: can't parse int", e); + throw new BrokenJsonException("Error in \\u in string: can't parse int", e, i + 2); } default: - throw new BrokenJsonException("Unknown \\ sequence in char number " + i); + throw new BrokenJsonException("Unknown \\ sequence in char number " + i, i); } } @@ -410,4 +410,14 @@ private static void createJson(Map data, StringBuilder builder) throw } class JsonTerminatedException extends Exception { + private int offset; + + public JsonTerminatedException(int index) { + super(); + offset = index; + } + + public int getOffset() { + return offset; + } } From c1defc4f2469ad96bc4c434d0e313b40cdca19f5 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sat, 29 Nov 2014 13:37:59 +0300 Subject: [PATCH 10/36] Developing (commit to chenge branch) --- .../LebedevAleksey/storeable/Database.java | 19 ++++++++++------- .../LebedevAleksey/storeable/Storeable.java | 21 +++++++++++++++++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index cfbb9348c..feced8058 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -4,6 +4,8 @@ import ru.fizteh.fivt.storage.structured.Storeable; import ru.fizteh.fivt.storage.structured.Table; import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; import sun.reflect.generics.reflectiveObjects.NotImplementedException; import java.io.DataOutputStream; @@ -13,6 +15,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.text.ParseException; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -134,9 +137,11 @@ public void removeTable(String name) throws IOException { @Override public Storeable deserialize(Table table, String value) throws ParseException { - - //TODO - throw new NotImplementedException(); + try { + JsonParser.parseJson(value); + } catch (BrokenJsonException e) { + throw new ParseException("Can't parse JSON: " + e.getMessage(), e.getOffsetError()); + } return null; } @@ -149,14 +154,12 @@ public String serialize(Table table, Storeable value) throws ColumnFormatExcepti @Override public Storeable createFor(Table table) { - return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable(new Object[table.getColumnsCount()], - table); + return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable(Arrays.asList( + new Object[table.getColumnsCount()]), table); } @Override public Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException { - //TODO - throw new NotImplementedException(); - return null; + return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable(values, table); } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java index 9cb551524..f1d3a5ab3 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java @@ -7,10 +7,27 @@ public class Storeable implements ru.fizteh.fivt.storage.structured.Storeable { public static final String INCORRECT_TYPE_MESSAGE = "Value type isn't correct."; - private List data; + private List data; private Table table; - public Storeable(List data, Table table) { + public Storeable(List data, Table table) throws ColumnFormatException { + if (data == null) { + throw new IllegalArgumentException("Argument \"data\" is null"); + } + if (table == null) { + throw new IllegalArgumentException("Argument \"table\" is null"); + } + if (data.size() != table.getColumnsCount()) { + throw new IndexOutOfBoundsException("Argument arrays are different sizes"); + } + for (int i = 0; i < data.size(); ++i) { + Object item = data.get(i); + if (item != null) { + if (item.getClass() != table.getColumnType(i)) { + throw new ColumnFormatException("Wrong type of column number " + i); + } + } + } this.data = data; this.table = table; } From be235570a5f5ab7b275195675d387f6dfd5d909c Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sat, 29 Nov 2014 13:37:18 +0300 Subject: [PATCH 11/36] Add offset number to JSON --- .../storeable/json/BrokenJsonException.java | 13 ++--- .../storeable/json/JsonParser.java | 48 +++++++++++-------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java index edf28e637..b9f4a23f9 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java @@ -1,18 +1,19 @@ package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; public class BrokenJsonException extends Exception { - public BrokenJsonException() { - } + private int offsetError; - public BrokenJsonException(String message) { + public BrokenJsonException(String message, int offsetError) { super(message); + this.offsetError = offsetError; } - public BrokenJsonException(String message, Throwable cause) { + public BrokenJsonException(String message, Throwable cause, int offsetError) { super(message, cause); + this.offsetError = offsetError; } - public BrokenJsonException(Throwable cause) { - super(cause); + public int getOffsetError() { + return offsetError; } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java index d7a903ebb..621bf7fa4 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -25,14 +25,14 @@ public static String getJson(Object data) throws JsonUnsopportedObjectException public static Object parseJson(String json) throws BrokenJsonException { json = json.trim(); - Pair result = null; + Pair result; try { result = parseJson(json, 0, null); } catch (JsonTerminatedException e) { - throw new BrokenJsonException("Wrong JSON"); + throw new BrokenJsonException("Wrong JSON", json.length() > 0 ? json.length() - 1 : 0); } if (result.getValue() != json.length()) { - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, result.getValue()); } return result.getKey(); } @@ -43,7 +43,7 @@ public static Pair parseJson(String json, int begin, Character for (i = begin; i < json.length(); i++) { char symbol = json.charAt(i); if (possibleTerminator != null && possibleTerminator.equals(symbol)) { - throw new JsonTerminatedException(); + throw new JsonTerminatedException(i); } switch (symbol) { case ' ': @@ -79,19 +79,19 @@ public static Pair parseJson(String json, int begin, Character if ("false".equals(json.substring(i, i + 5))) { return new Pair<>(false, i + 5); } else { - throw new BrokenJsonException("Unexpected symbol in position " + i + 4); + throw new BrokenJsonException("Unexpected symbol in position " + i + 4, i + 4); } default: - throw new BrokenJsonException("Wrong token"); + throw new BrokenJsonException("Wrong token", i); } } catch (IndexOutOfBoundsException e) { - throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, e); + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, e, i); } default: - throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i); + throw new BrokenJsonException("Unexpected symbol \"" + symbol + "\" in position " + i, i); } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, i); } private static Pair parseMapFromJson(String json, int begin) throws BrokenJsonException { @@ -108,7 +108,7 @@ private static Pair parseMapFromJson(String json, int begin) th try { key = (String) result.getKey(); } catch (ClassCastException e) { - throw new BrokenJsonException("Wrong map key"); + throw new BrokenJsonException("Wrong map key", i); } i = result.getValue(); boolean exited = false; @@ -127,7 +127,7 @@ private static Pair parseMapFromJson(String json, int begin) th try { result = parseJson(json, i, null); } catch (JsonTerminatedException e) { - throw new BrokenJsonException("JSON is not correct"); + throw new BrokenJsonException("JSON is not correct", e.getOffset()); } map.put(key, result.getKey()); i = result.getValue(); @@ -147,7 +147,7 @@ private static Pair parseMapFromJson(String json, int begin) th } } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, i); } private static Pair searchNextChar(String json, Object value, int i, char mask) { @@ -186,7 +186,7 @@ private static Pair parseArrayFromJson(String json, int begin) } } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, i); } private static Pair parseNumberFromJson(String json, int begin) throws BrokenJsonException { @@ -233,7 +233,7 @@ private static Pair parseNumberFromJson(String json, int begin) } catch (NumberFormatException e) { // Next type... } - throw new BrokenJsonException("Wrong number"); + throw new BrokenJsonException("Wrong number", i); } private static Pair parseStringFromJson(String json, int begin) @@ -251,12 +251,12 @@ private static Pair parseStringFromJson(String json, int begin) } } } - throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE); + throw new BrokenJsonException(UNEXPECTED_STOP_MESSAGE, json.length() - 1); } private static int parseSlashSequense(String json, StringBuilder builder, int i) throws BrokenJsonException { if (i + 1 == json.length()) { - throw new BrokenJsonException("String doesn't finish"); + throw new BrokenJsonException("String doesn't finish", i); } switch (json.charAt(i + 1)) { case '"': @@ -285,17 +285,17 @@ private static int parseSlashSequense(String json, StringBuilder builder, int i) return i + 1; case 'u': if (i + 5 >= json.length()) { - throw new BrokenJsonException("Error in \\u in string"); + throw new BrokenJsonException("Error in \\u in string", i); } try { int num = Integer.parseInt(json.substring(i + 2, i + 6), 16); builder.append((char) num); return i + 5; } catch (NumberFormatException e) { - throw new BrokenJsonException("Error in \\u in string: can't parse int", e); + throw new BrokenJsonException("Error in \\u in string: can't parse int", e, i + 2); } default: - throw new BrokenJsonException("Unknown \\ sequence in char number " + i); + throw new BrokenJsonException("Unknown \\ sequence in char number " + i, i); } } @@ -407,4 +407,14 @@ private static void createJson(Map data, StringBuilder builder) throw } class JsonTerminatedException extends Exception { + private int offset; + + public JsonTerminatedException(int index) { + super(); + offset = index; + } + + public int getOffset() { + return offset; + } } From a5928cb89b1d8de8033b1862e84a20600cd0ca87 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sat, 29 Nov 2014 13:58:23 +0300 Subject: [PATCH 12/36] Add one test --- .../storeable/tests/JsonParserTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java index ea233ba33..853a6c1c4 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java @@ -416,4 +416,15 @@ public void testWrongMapSerialise() throws JsonUnsopportedObjectException { map.put(1, 2); JsonParser.getJson(map); } + + @Test + public void testBrokenJSONExceptionCanSaveOffset() { + BrokenJsonException ex = new BrokenJsonException("Test", 7); + Assert.assertEquals("Test", ex.getMessage()); + Assert.assertEquals(7, ex.getOffsetError()); + ex = new BrokenJsonException("Message", 42); + Assert.assertEquals("Message", ex.getMessage()); + Assert.assertEquals(42, ex.getOffsetError()); + } + } From 760b8840e569b47a78529036503a8de13e12accb Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sat, 29 Nov 2014 13:58:23 +0300 Subject: [PATCH 13/36] Add one test --- .../storeable/tests/JsonParserTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java index ea233ba33..853a6c1c4 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java @@ -416,4 +416,15 @@ public void testWrongMapSerialise() throws JsonUnsopportedObjectException { map.put(1, 2); JsonParser.getJson(map); } + + @Test + public void testBrokenJSONExceptionCanSaveOffset() { + BrokenJsonException ex = new BrokenJsonException("Test", 7); + Assert.assertEquals("Test", ex.getMessage()); + Assert.assertEquals(7, ex.getOffsetError()); + ex = new BrokenJsonException("Message", 42); + Assert.assertEquals("Message", ex.getMessage()); + Assert.assertEquals(42, ex.getOffsetError()); + } + } From e5bbfe451e4530899a6c9cc05058388149cd0f38 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 01:09:43 +0300 Subject: [PATCH 14/36] Rename to remove spelling mistake --- .../storeable/json/JsonParser.java | 16 ++++++++-------- .../json/JsonUnsopportedObjectException.java | 18 ------------------ .../json/JsonUnsupportedObjectException.java | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java index 8d2d1b42f..a8acf954a 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -17,7 +17,7 @@ public static void setNeedShortIntegerTypes(boolean needShortIntegerTypes) { JsonParser.NEED_SHORT_INTEGER_TYPES.set(needShortIntegerTypes); } - public static String getJson(Object data) throws JsonUnsopportedObjectException { + public static String getJson(Object data) throws JsonUnsupportedObjectException { StringBuilder result = new StringBuilder(); createJson(data, result); return result.toString(); @@ -302,7 +302,7 @@ private static int parseSlashSequense(String json, StringBuilder builder, int i) } } - private static void createJson(Object data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Object data, StringBuilder builder) throws JsonUnsupportedObjectException { if (data == null) { builder.append("null"); return; @@ -312,7 +312,7 @@ private static void createJson(Object data, StringBuilder builder) throws JsonUn try { map = (Map) data; } catch (ClassCastException e) { - throw new JsonUnsopportedObjectException("Only Map is supported, when you use map", e); + throw new JsonUnsupportedObjectException("Only Map is supported, when you use map", e); } createJson(map, builder); return; @@ -337,7 +337,7 @@ private static void createJson(Object data, StringBuilder builder) throws JsonUn builder.append(((boolean) data) ? "true" : false); return; } - throw new JsonUnsopportedObjectException("This type (" + data.getClass() + ") is unsupported by JSON."); + throw new JsonUnsupportedObjectException("This type (" + data.getClass() + ") is unsupported by JSON."); } private static void createJson(String data, StringBuilder builder) { @@ -359,7 +359,7 @@ private static boolean containsInterface(Object data, Class type) { } } - private static void createJson(Iterable data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Iterable data, StringBuilder builder) throws JsonUnsupportedObjectException { builder.append("["); boolean first = true; for (Object item : data) { @@ -373,7 +373,7 @@ private static void createJson(Iterable data, StringBuilder builder) throws Json builder.append("]"); } - private static void createJson(Object[] data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Object[] data, StringBuilder builder) throws JsonUnsupportedObjectException { builder.append("["); boolean first = true; for (Object item : data) { @@ -387,7 +387,7 @@ private static void createJson(Object[] data, StringBuilder builder) throws Json builder.append("]"); } - private static void createJson(Map data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Map data, StringBuilder builder) throws JsonUnsupportedObjectException { try { builder.append("{"); Set keys = data.keySet(); @@ -404,7 +404,7 @@ private static void createJson(Map data, StringBuilder builder) throw } builder.append("}"); } catch (ClassCastException e) { - throw new JsonUnsopportedObjectException("Only Map is supported.", e); + throw new JsonUnsupportedObjectException("Only Map is supported.", e); } } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java deleted file mode 100644 index dc65bc239..000000000 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; - -public class JsonUnsopportedObjectException extends Exception { - public JsonUnsopportedObjectException() { - } - - public JsonUnsopportedObjectException(String message) { - super(message); - } - - public JsonUnsopportedObjectException(String message, Throwable cause) { - super(message, cause); - } - - public JsonUnsopportedObjectException(Throwable cause) { - super(cause); - } -} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java new file mode 100644 index 000000000..0316d7259 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; + +public class JsonUnsupportedObjectException extends Exception { + public JsonUnsupportedObjectException() { + } + + public JsonUnsupportedObjectException(String message) { + super(message); + } + + public JsonUnsupportedObjectException(String message, Throwable cause) { + super(message, cause); + } + + public JsonUnsupportedObjectException(Throwable cause) { + super(cause); + } +} From 025d749bd94d4d62bfb2faf41d1ac788d9f19c1d Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 01:10:18 +0300 Subject: [PATCH 15/36] JSON renaming in tests --- .../storeable/tests/JsonParserTest.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java index 853a6c1c4..9d68c68a9 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java @@ -4,21 +4,21 @@ import org.junit.Test; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; -import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsopportedObjectException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsupportedObjectException; import java.io.File; import java.util.*; public class JsonParserTest { @Test - public void testGetJsonForStrings() throws JsonUnsopportedObjectException { + public void testGetJsonForStrings() throws JsonUnsupportedObjectException { Assert.assertEquals("\"Qwerty\"", JsonParser.getJson("Qwerty")); Assert.assertEquals("\"Test text\"", JsonParser.getJson("Test text")); Assert.assertEquals("\"Text, fore test. Asdf!\"", JsonParser.getJson("Text, fore test. Asdf!")); } @Test - public void testGetJsonForStringsWithCaracters() throws JsonUnsopportedObjectException { + public void testGetJsonForStringsWithCaracters() throws JsonUnsupportedObjectException { Assert.assertEquals("\"Qwe\\nrty\"", JsonParser.getJson("Qwe\nrty")); Assert.assertEquals("\"Qwe\\re r\\ty\"", JsonParser.getJson("Qwe\re r\ty")); Assert.assertEquals("\"This is quote: \\\"test\\\".\"", JsonParser.getJson("This is quote: \"test\".")); @@ -26,14 +26,14 @@ public void testGetJsonForStringsWithCaracters() throws JsonUnsopportedObjectExc Assert.assertEquals("\"\\f\"", JsonParser.getJson("\f")); } - @Test(expected = JsonUnsopportedObjectException.class) - public void testThrowExceptionForUnknownTypes() throws JsonUnsopportedObjectException { + @Test(expected = JsonUnsupportedObjectException.class) + public void testThrowExceptionForUnknownTypes() throws JsonUnsupportedObjectException { JsonParser.getJson(new File(".")); } @Test() - public void testCanSerialiseIntegers() throws JsonUnsopportedObjectException { + public void testCanSerialiseIntegers() throws JsonUnsupportedObjectException { Assert.assertEquals("1234", JsonParser.getJson(1234)); Assert.assertEquals("123456789012345", JsonParser.getJson(123456789012345L)); Assert.assertEquals("-4567", JsonParser.getJson(-4567)); @@ -49,30 +49,30 @@ public void testCanSerialiseIntegers() throws JsonUnsopportedObjectException { } @Test() - public void testCanSerialiseBools() throws JsonUnsopportedObjectException { + public void testCanSerialiseBools() throws JsonUnsupportedObjectException { Assert.assertEquals("true", JsonParser.getJson(true)); Assert.assertEquals("false", JsonParser.getJson(false)); } @Test() - public void testCanSerialiseNull() throws JsonUnsopportedObjectException { + public void testCanSerialiseNull() throws JsonUnsupportedObjectException { Assert.assertEquals("null", JsonParser.getJson(null)); } @Test - public void testCanGetJsonForArrays() throws JsonUnsopportedObjectException { + public void testCanGetJsonForArrays() throws JsonUnsupportedObjectException { Assert.assertEquals("[123,\"QwertY\"]", JsonParser.getJson(Arrays.asList(new Object[]{123, "QwertY"}))); Assert.assertEquals("[123,\"QwertY\",true]", JsonParser.getJson(new Object[]{123, "QwertY", true})); } @Test - public void testCanGetJsonForMap() throws JsonUnsopportedObjectException { + public void testCanGetJsonForMap() throws JsonUnsupportedObjectException { checkMap(new TreeMap<>()); checkMap(new HashMap<>()); } - private void checkMap(Map map) throws JsonUnsopportedObjectException { + private void checkMap(Map map) throws JsonUnsupportedObjectException { map.put("b", true); map.put("c", null); map.put("dd", 3.1415); @@ -101,7 +101,7 @@ private void checkMap(Map map) throws JsonUnsopportedObjectExcep } private int assertMapJson(Map map, String[] strings, int equalsCount) - throws JsonUnsopportedObjectException { + throws JsonUnsupportedObjectException { if (("{" + strings[0] + "," + strings[1] + "," + strings[2] + "}").equals(JsonParser.getJson(map))) { ++equalsCount; } @@ -109,7 +109,7 @@ private int assertMapJson(Map map, String[] strings, int equalsC } @Test - public void testSubItemJson() throws JsonUnsopportedObjectException { + public void testSubItemJson() throws JsonUnsupportedObjectException { Map map = new TreeMap<>(); map.put("a", 1); Object[] array = new Object[]{1, map, new Object[]{1.2, new Object[]{true}, null}}; @@ -117,7 +117,7 @@ public void testSubItemJson() throws JsonUnsopportedObjectException { } @Test - public void testCanJsonSupportedArrays() throws JsonUnsopportedObjectException { + public void testCanJsonSupportedArrays() throws JsonUnsupportedObjectException { Integer[] array = new Integer[]{1, 2, 3}; Assert.assertEquals("[1,2,3]", (String) JsonParser.getJson(array)); } @@ -410,8 +410,8 @@ public void testNotTerminatedString() throws BrokenJsonException { } - @Test(expected = JsonUnsopportedObjectException.class) - public void testWrongMapSerialise() throws JsonUnsopportedObjectException { + @Test(expected = JsonUnsupportedObjectException.class) + public void testWrongMapSerialise() throws JsonUnsupportedObjectException { Map map = new TreeMap<>(); map.put(1, 2); JsonParser.getJson(map); From a731642413846d1714ec03f5b921698cb150c392 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 01:10:33 +0300 Subject: [PATCH 16/36] Storeable Table --- .../LebedevAleksey/storeable/Database.java | 134 ++++++++++-- .../storeable/DatabaseFactory.java | 16 ++ .../LebedevAleksey/storeable/Storeable.java | 4 +- .../storeable/StoreableTable.java | 195 ++++++++++-------- 4 files changed, 236 insertions(+), 113 deletions(-) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseFactory.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index feced8058..c2a431474 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -6,19 +6,13 @@ import ru.fizteh.fivt.storage.structured.TableProvider; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsupportedObjectException; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.text.ParseException; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; public class Database implements TableProvider { public static final String TABLE_SIGNATURE_FILE_NAME = "signature.tsv"; @@ -44,12 +38,48 @@ public class Database implements TableProvider { } - private final Path directoryPath; + private Path directoryPath; private Map tables = new TreeMap<>(); - public Database(String directory) { - directoryPath = new File(directory).toPath(); + public Database(String directory) throws IOException { + File root = new File(directory); + directoryPath = root.toPath(); + File[] tables = root.listFiles(); + for (File file : tables) { + if (file.isDirectory()) { + File signature = file.toPath().resolve(TABLE_SIGNATURE_FILE_NAME).toFile(); + String tablename = file.getName(); + if (signature.exists() && signature.isFile()) { + String signatureString; + try (FileInputStream stream = new FileInputStream(signature.getAbsolutePath())) { + try (DataInputStream signaturedata = new DataInputStream(stream)) { + signatureString = signaturedata.readUTF(); + } + } + String[] tokens = signatureString.split(" "); + List> types = new ArrayList<>(); + for (String item : tokens) { + Class type = stringTypesMap.get(item); + if (type == null) { + throw new IOException("Wrong type name in signature of table " + tablename); + } else { + types.add(type); + } + } + StoreableTable table = new StoreableTable(tablename, this, types); + this.tables.put(tablename, table); + } else { + throw new IOException("Where is not signature file in table " + tablename); + } + } else { + fileFoundInRootDirectory(file); + } + } + } + + protected void fileFoundInRootDirectory(File file) throws IOException { + throw new IOException("There is file " + file.getName() + " in root directory"); } private void assertNameNotNull(String name) { @@ -77,7 +107,8 @@ public Table createTable(String name, List> columnTypes) throws IOExcep String tableSignature = createTableSignature(columnTypes); try { Files.createDirectory(path); - try (FileOutputStream stream = new FileOutputStream(path.resolve(TABLE_SIGNATURE_FILE_NAME).toString())) { + try (FileOutputStream stream = new FileOutputStream(path.resolve( + TABLE_SIGNATURE_FILE_NAME).toString())) { try (DataOutputStream dataStream = new DataOutputStream(stream)) { dataStream.writeUTF(tableSignature); dataStream.flush(); @@ -124,7 +155,7 @@ public Path getRootDirectoryPath() { } private StoreableTable generateTable(String name) { - return new StoreableTable(name, this); + return new StoreableTable(name, this, types); } @Override @@ -137,19 +168,82 @@ public void removeTable(String name) throws IOException { @Override public Storeable deserialize(Table table, String value) throws ParseException { + List data; try { - JsonParser.parseJson(value); + data = (List) JsonParser.parseJson(value); } catch (BrokenJsonException e) { throw new ParseException("Can't parse JSON: " + e.getMessage(), e.getOffsetError()); + } catch (ClassCastException e) { + throw new ParseException("Wrong JSON: not a list", 0); + } + if (data.size() == table.getColumnsCount()) { + Storeable storable = createFor(table); + for (int i = 0; i < data.size(); i++) { + if (data.get(i) == null) { + storable.setColumnAt(i, 0); + } else if (data.get(i).getClass() == table.getColumnType(i)) { + storable.setColumnAt(i, data.get(i)); + } else if (!tryCastInteger(table, i, data.get(i), storable) && + !tryCastFloat(table, i, data.get(i), storable)) { + throw new ParseException("Wrong data type in column number " + i, -1); + } + } + return storable; + } else { + throw new ParseException("Wrong size of list: have " + data.size() + ", table have " + + table.getColumnsCount() + " columns.", value.length() - 1); } - return null; + } + + private boolean tryCastInteger(Table table, int column, Object value, Storeable result) { + if (table.getColumnType(column) == Integer.class || table.getColumnType(column) == Byte.class) { + if (value.getClass() == Long.class) { + if (table.getColumnType(column) == Integer.class) { + long val = (long) value; + if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) { + result.setColumnAt(column, (int) value); + return true; + } + } else { + if (table.getColumnType(column) == Byte.class) { + long val = (long) value; + if (val >= Byte.MIN_VALUE && val <= Byte.MAX_VALUE) { + result.setColumnAt(column, (byte) val); + return true; + } + } + } + } + } + return false; + } + + private boolean tryCastFloat(Table table, int column, Object value, Storeable result) { + if (table.getColumnType(column) == Float.class) { + if (value.getClass() == Double.class) { + if (table.getColumnType(column) == Float.class) { + double val = (double) value; + if (val >= Float.MIN_VALUE && val <= Float.MAX_VALUE) { + result.setColumnAt(column, (float) val); + return true; + } + } + } + } + return false; } @Override public String serialize(Table table, Storeable value) throws ColumnFormatException { - //TODO - throw new NotImplementedException(); - return null; + List data = new ArrayList<>(); + for (int i = 0; i < table.getColumnsCount(); i++) { + data.add(value.getColumnAt(i)); + } + try { + return JsonParser.getJson(data); + } catch (JsonUnsupportedObjectException e) { + throw new ColumnFormatException("Unknown column format", e); + } } @Override @@ -160,6 +254,6 @@ public Storeable createFor(Table table) { @Override public Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException { - return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable(values, table); + return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable((List) values, table); } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseFactory.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseFactory.java new file mode 100644 index 000000000..74ce5407c --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseFactory.java @@ -0,0 +1,16 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable; + +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.storage.structured.TableProviderFactory; + +import java.io.IOException; + +/** + * Created by Алексей on 07.12.2014. + */ +public class DatabaseFactory implements TableProviderFactory { + @Override + public TableProvider create(String path) throws IOException { + return new Database(path); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java index f1d3a5ab3..ee4e4192d 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java @@ -7,10 +7,10 @@ public class Storeable implements ru.fizteh.fivt.storage.structured.Storeable { public static final String INCORRECT_TYPE_MESSAGE = "Value type isn't correct."; - private List data; + private List data; private Table table; - public Storeable(List data, Table table) throws ColumnFormatException { + public Storeable(List data, Table table) throws ColumnFormatException { if (data == null) { throw new IllegalArgumentException("Argument \"data\" is null"); } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java index c55454c27..3c885f9a6 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java @@ -5,156 +5,169 @@ import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.LoadOrSaveException; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Table; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; +import ru.fizteh.fivt.students.LebedevAleksey.junit.DatabaseException; -import java.util.*; -import java.util.function.Consumer; +import java.io.IOException; +import java.text.ParseException; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; public class StoreableTable implements ru.fizteh.fivt.storage.structured.Table { + private final String name; + private final Database database; private Map changedKeys = new TreeMap<>(); - private Table stringTable; + private List> columnTypes; - public StoreableTable(String name, Database databaseParent) { - Table stringTable=new Table(name, databaseParent.getRootDirectoryPath()); - } - - @Override - public Storeable put(String key, Storeable value) throws ColumnFormatException { - return null; + public StoreableTable(String name, Database databaseParent, List> types) { + this.name = name; + database = databaseParent; + columnTypes = types; + Table stringTable = new Table(name, databaseParent.getRootDirectoryPath()); } - @Override - public boolean remove(String key) throws LoadOrSaveException, DatabaseFileStructureException { - String value = super.get(key); - String oldValue = get(key); - if (value != null) { - if (oldValue != null) { - changedKeys.put(key, null); - } - } else { - changedKeys.remove(key); + private void checkKeyNotNull(String key) { + if (key == null) { + throw new IllegalArgumentException("Argument \"key\" is null"); } - return (oldValue != null); } - @Override - public int size() { - return 0; + private void checkKeyValueNotNull(String key, Storeable value) { + checkKeyNotNull(key); + if (value == null) { + throw new IllegalArgumentException("Argument \"value\" is null"); + } } - public String getAndRemove(String key) throws DatabaseFileStructureException, LoadOrSaveException { - String oldValue = get(key); - remove(key); + private String putStrings(String key, String value) throws DatabaseFileStructureException, LoadOrSaveException { + String oldValue = getString(key); + if (value.equals(stringTable.get(key))) { + changedKeys.remove(key); + } else { + changedKeys.put(key, value); + } return oldValue; } - @Override - public String getName() { - return null; - } - - @Override - public String get(String key) throws LoadOrSaveException, DatabaseFileStructureException { + private String getString(String key) throws LoadOrSaveException, DatabaseFileStructureException { if (changedKeys.containsKey(key)) { return changedKeys.get(key); } else { - return super.get(key); + return stringTable.get(key); } } @Override - public String put(String key, String value) throws LoadOrSaveException, DatabaseFileStructureException { - String oldValue = get(key); - if (value.equals(super.get(key))) { - changedKeys.remove(key); - } else { - changedKeys.put(key, value); + public Storeable put(String key, Storeable value) throws ColumnFormatException { + checkKeyValueNotNull(key, value); + try { + return database.deserialize(this, putStrings(key, database.serialize(this, value))); + } catch (DatabaseFileStructureException | LoadOrSaveException e) { + throw new DatabaseException(e); + } catch (ParseException e) { + throw new ColumnFormatException(e); } - return oldValue; } @Override - public void save() throws LoadOrSaveException, DatabaseFileStructureException { - for (String key : changedKeys.keySet()) { - String value = changedKeys.get(key); - if (value == null) { - super.remove(key); - } else { - super.put(key, value); + public Storeable remove(String key) { + checkKeyNotNull(key); + String value; + try { + value = stringTable.get(key); + } catch (LoadOrSaveException | DatabaseFileStructureException e) { + throw new DatabaseException(e); + } + Storeable oldValue = get(key); + if (value != null) { + if (oldValue != null) { + changedKeys.put(key, null); } + } else { + changedKeys.remove(key); } - changedKeys.clear(); - super.save(); + return oldValue; } @Override - public int count() throws LoadOrSaveException, DatabaseFileStructureException { - int deletedCount = 0; - int addedCount = 0; - for (String key : changedKeys.keySet()) { - String value = changedKeys.get(key); - if (value == null) { - ++deletedCount; - } else { - if (super.get(key) == null) { - addedCount++; + public int size() { + try { + int deletedCount = 0; + int addedCount = 0; + for (String key : changedKeys.keySet()) { + String value = changedKeys.get(key); + if (value == null) { + ++deletedCount; + } else { + if (stringTable.get(key) == null) { + addedCount++; + } } } + return stringTable.count() + addedCount - deletedCount; + } catch (LoadOrSaveException | DatabaseFileStructureException e) { + throw new DatabaseException(e); } - return super.count() + addedCount - deletedCount; } @Override - public ArrayList list() throws LoadOrSaveException, DatabaseFileStructureException { - Set items = new TreeSet<>(super.list()); - for (String key : changedKeys.keySet()) { - String value = changedKeys.get(key); - if (value == null) { - items.remove(key); - } else { - items.add(key); + public int commit() throws IOException { + int changes = changesCount(); + try { + for (String key : changedKeys.keySet()) { + String value = changedKeys.get(key); + if (value == null) { + stringTable.remove(key); + } else { + stringTable.put(key, value); + } } + changedKeys.clear(); + stringTable.save(); + } catch (DatabaseFileStructureException | LoadOrSaveException e) { + throw new IOException(e.getMessage(), e); } - ArrayList result = new ArrayList<>(items.size()); - items.forEach(new Consumer() { - @Override - public void accept(String s) { - result.add(s); - } - }); - return result; + return 0; } public int changesCount() { return changedKeys.size(); } - public int commit(){ - int changes = changesCount(); - save(); - return changes; - } - + @Override public int rollback() { int changes = changesCount(); changedKeys.clear(); - initParts(); return changes; } + @Override + public String getName() { + return name; + } + + @Override + public Storeable get(String key) { + checkKeyNotNull(key); + try { + return database.deserialize(this, getString(key)); + } catch (ParseException | LoadOrSaveException | DatabaseFileStructureException e) { + throw new DatabaseException(e); + } + } + @Override public int getColumnsCount() { - return 0; + return columnTypes.size(); } @Override public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { - return null; + return columnTypes.get(columnIndex); } - public void drop() { - //TODO - throw new NotImplementedException(); + public void drop() throws DatabaseFileStructureException, LoadOrSaveException { + stringTable.drop(); } } From d44245fa91b4a652d3be4c30346ff9df89c3c9a3 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 01:10:18 +0300 Subject: [PATCH 17/36] JSON renaming in tests --- .../storeable/tests/JsonParserTest.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java index 853a6c1c4..9d68c68a9 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/JsonParserTest.java @@ -4,21 +4,21 @@ import org.junit.Test; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; -import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsopportedObjectException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsupportedObjectException; import java.io.File; import java.util.*; public class JsonParserTest { @Test - public void testGetJsonForStrings() throws JsonUnsopportedObjectException { + public void testGetJsonForStrings() throws JsonUnsupportedObjectException { Assert.assertEquals("\"Qwerty\"", JsonParser.getJson("Qwerty")); Assert.assertEquals("\"Test text\"", JsonParser.getJson("Test text")); Assert.assertEquals("\"Text, fore test. Asdf!\"", JsonParser.getJson("Text, fore test. Asdf!")); } @Test - public void testGetJsonForStringsWithCaracters() throws JsonUnsopportedObjectException { + public void testGetJsonForStringsWithCaracters() throws JsonUnsupportedObjectException { Assert.assertEquals("\"Qwe\\nrty\"", JsonParser.getJson("Qwe\nrty")); Assert.assertEquals("\"Qwe\\re r\\ty\"", JsonParser.getJson("Qwe\re r\ty")); Assert.assertEquals("\"This is quote: \\\"test\\\".\"", JsonParser.getJson("This is quote: \"test\".")); @@ -26,14 +26,14 @@ public void testGetJsonForStringsWithCaracters() throws JsonUnsopportedObjectExc Assert.assertEquals("\"\\f\"", JsonParser.getJson("\f")); } - @Test(expected = JsonUnsopportedObjectException.class) - public void testThrowExceptionForUnknownTypes() throws JsonUnsopportedObjectException { + @Test(expected = JsonUnsupportedObjectException.class) + public void testThrowExceptionForUnknownTypes() throws JsonUnsupportedObjectException { JsonParser.getJson(new File(".")); } @Test() - public void testCanSerialiseIntegers() throws JsonUnsopportedObjectException { + public void testCanSerialiseIntegers() throws JsonUnsupportedObjectException { Assert.assertEquals("1234", JsonParser.getJson(1234)); Assert.assertEquals("123456789012345", JsonParser.getJson(123456789012345L)); Assert.assertEquals("-4567", JsonParser.getJson(-4567)); @@ -49,30 +49,30 @@ public void testCanSerialiseIntegers() throws JsonUnsopportedObjectException { } @Test() - public void testCanSerialiseBools() throws JsonUnsopportedObjectException { + public void testCanSerialiseBools() throws JsonUnsupportedObjectException { Assert.assertEquals("true", JsonParser.getJson(true)); Assert.assertEquals("false", JsonParser.getJson(false)); } @Test() - public void testCanSerialiseNull() throws JsonUnsopportedObjectException { + public void testCanSerialiseNull() throws JsonUnsupportedObjectException { Assert.assertEquals("null", JsonParser.getJson(null)); } @Test - public void testCanGetJsonForArrays() throws JsonUnsopportedObjectException { + public void testCanGetJsonForArrays() throws JsonUnsupportedObjectException { Assert.assertEquals("[123,\"QwertY\"]", JsonParser.getJson(Arrays.asList(new Object[]{123, "QwertY"}))); Assert.assertEquals("[123,\"QwertY\",true]", JsonParser.getJson(new Object[]{123, "QwertY", true})); } @Test - public void testCanGetJsonForMap() throws JsonUnsopportedObjectException { + public void testCanGetJsonForMap() throws JsonUnsupportedObjectException { checkMap(new TreeMap<>()); checkMap(new HashMap<>()); } - private void checkMap(Map map) throws JsonUnsopportedObjectException { + private void checkMap(Map map) throws JsonUnsupportedObjectException { map.put("b", true); map.put("c", null); map.put("dd", 3.1415); @@ -101,7 +101,7 @@ private void checkMap(Map map) throws JsonUnsopportedObjectExcep } private int assertMapJson(Map map, String[] strings, int equalsCount) - throws JsonUnsopportedObjectException { + throws JsonUnsupportedObjectException { if (("{" + strings[0] + "," + strings[1] + "," + strings[2] + "}").equals(JsonParser.getJson(map))) { ++equalsCount; } @@ -109,7 +109,7 @@ private int assertMapJson(Map map, String[] strings, int equalsC } @Test - public void testSubItemJson() throws JsonUnsopportedObjectException { + public void testSubItemJson() throws JsonUnsupportedObjectException { Map map = new TreeMap<>(); map.put("a", 1); Object[] array = new Object[]{1, map, new Object[]{1.2, new Object[]{true}, null}}; @@ -117,7 +117,7 @@ public void testSubItemJson() throws JsonUnsopportedObjectException { } @Test - public void testCanJsonSupportedArrays() throws JsonUnsopportedObjectException { + public void testCanJsonSupportedArrays() throws JsonUnsupportedObjectException { Integer[] array = new Integer[]{1, 2, 3}; Assert.assertEquals("[1,2,3]", (String) JsonParser.getJson(array)); } @@ -410,8 +410,8 @@ public void testNotTerminatedString() throws BrokenJsonException { } - @Test(expected = JsonUnsopportedObjectException.class) - public void testWrongMapSerialise() throws JsonUnsopportedObjectException { + @Test(expected = JsonUnsupportedObjectException.class) + public void testWrongMapSerialise() throws JsonUnsupportedObjectException { Map map = new TreeMap<>(); map.put(1, 2); JsonParser.getJson(map); From 9ff9c65c29d68d103c56fefb684ebe2d5490b562 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 01:09:43 +0300 Subject: [PATCH 18/36] Rename to remove spelling mistake --- .../storeable/json/JsonParser.java | 16 ++++++++-------- .../json/JsonUnsopportedObjectException.java | 18 ------------------ .../json/JsonUnsupportedObjectException.java | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 26 deletions(-) delete mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java index 621bf7fa4..0a6123f3d 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -17,7 +17,7 @@ public static void setNeedShortIntegerTypes(boolean needShortIntegerTypes) { JsonParser.NEED_SHORT_INTEGER_TYPES.set(needShortIntegerTypes); } - public static String getJson(Object data) throws JsonUnsopportedObjectException { + public static String getJson(Object data) throws JsonUnsupportedObjectException { StringBuilder result = new StringBuilder(); createJson(data, result); return result.toString(); @@ -299,7 +299,7 @@ private static int parseSlashSequense(String json, StringBuilder builder, int i) } } - private static void createJson(Object data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Object data, StringBuilder builder) throws JsonUnsupportedObjectException { if (data == null) { builder.append("null"); return; @@ -309,7 +309,7 @@ private static void createJson(Object data, StringBuilder builder) throws JsonUn try { map = (Map) data; } catch (ClassCastException e) { - throw new JsonUnsopportedObjectException("Only Map is supported, when you use map", e); + throw new JsonUnsupportedObjectException("Only Map is supported, when you use map", e); } createJson(map, builder); return; @@ -334,7 +334,7 @@ private static void createJson(Object data, StringBuilder builder) throws JsonUn builder.append(((boolean) data) ? "true" : false); return; } - throw new JsonUnsopportedObjectException("This type (" + data.getClass() + ") is unsupported by JSON."); + throw new JsonUnsupportedObjectException("This type (" + data.getClass() + ") is unsupported by JSON."); } private static void createJson(String data, StringBuilder builder) { @@ -356,7 +356,7 @@ private static boolean containsInterface(Object data, Class type) { } } - private static void createJson(Iterable data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Iterable data, StringBuilder builder) throws JsonUnsupportedObjectException { builder.append("["); boolean first = true; for (Object item : data) { @@ -370,7 +370,7 @@ private static void createJson(Iterable data, StringBuilder builder) throws Json builder.append("]"); } - private static void createJson(Object[] data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Object[] data, StringBuilder builder) throws JsonUnsupportedObjectException { builder.append("["); boolean first = true; for (Object item : data) { @@ -384,7 +384,7 @@ private static void createJson(Object[] data, StringBuilder builder) throws Json builder.append("]"); } - private static void createJson(Map data, StringBuilder builder) throws JsonUnsopportedObjectException { + private static void createJson(Map data, StringBuilder builder) throws JsonUnsupportedObjectException { try { builder.append("{"); Set keys = data.keySet(); @@ -401,7 +401,7 @@ private static void createJson(Map data, StringBuilder builder) throw } builder.append("}"); } catch (ClassCastException e) { - throw new JsonUnsopportedObjectException("Only Map is supported.", e); + throw new JsonUnsupportedObjectException("Only Map is supported.", e); } } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java deleted file mode 100644 index dc65bc239..000000000 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsopportedObjectException.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; - -public class JsonUnsopportedObjectException extends Exception { - public JsonUnsopportedObjectException() { - } - - public JsonUnsopportedObjectException(String message) { - super(message); - } - - public JsonUnsopportedObjectException(String message, Throwable cause) { - super(message, cause); - } - - public JsonUnsopportedObjectException(Throwable cause) { - super(cause); - } -} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java new file mode 100644 index 000000000..0316d7259 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonUnsupportedObjectException.java @@ -0,0 +1,18 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; + +public class JsonUnsupportedObjectException extends Exception { + public JsonUnsupportedObjectException() { + } + + public JsonUnsupportedObjectException(String message) { + super(message); + } + + public JsonUnsupportedObjectException(String message, Throwable cause) { + super(message, cause); + } + + public JsonUnsupportedObjectException(Throwable cause) { + super(cause); + } +} From 5ca9f54190a7044f720dc79b9326eb1f98cb7eb0 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 15:15:45 +0300 Subject: [PATCH 19/36] =?UTF-8?q?JUnit=20v1.3=20(add=20final=20to=20variab?= =?UTF-8?q?le)=D1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java b/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java index 104e84e63..d07044299 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/junit/Table.java @@ -96,7 +96,7 @@ public ArrayList list() throws LoadOrSaveException, DatabaseFileStructur items.add(key); } } - ArrayList result = new ArrayList<>(items.size()); + final ArrayList result = new ArrayList<>(items.size()); items.forEach(new Consumer() { @Override public void accept(String s) { @@ -119,7 +119,6 @@ public int commit() throws DatabaseFileStructureException, LoadOrSaveException { public int rollback() { int changes = changesCount(); changedKeys.clear(); - initParts(); return changes; } } From 70736ab7428d36db84f4001693149c55bb94a998 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 17:08:13 +0300 Subject: [PATCH 20/36] Strings Tests adapted for storeable (1 fails) --- .../LebedevAleksey/storeable/Database.java | 80 ++- .../storeable/DatabaseState.java | 16 +- .../LebedevAleksey/storeable/Main.java | 244 ++++----- .../storeable/StoreableTable.java | 56 ++- .../tests/DatabaseTestWithWrapper.java | 151 ++++++ .../storeable/tests/StringTableTest.java | 468 ++++++++++++++++++ .../storeable/tests/StringTableWrapper.java | 91 ++++ 7 files changed, 945 insertions(+), 161 deletions(-) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTestWithWrapper.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index c2a431474..498e71399 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -4,6 +4,8 @@ import ru.fizteh.fivt.storage.structured.Storeable; import ru.fizteh.fivt.storage.structured.Table; import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.LoadOrSaveException; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsupportedObjectException; @@ -13,12 +15,13 @@ import java.nio.file.Path; import java.text.ParseException; import java.util.*; +import java.util.function.BiConsumer; public class Database implements TableProvider { public static final String TABLE_SIGNATURE_FILE_NAME = "signature.tsv"; private static final String INCORRECT_NAME_OF_TABLES = "This name is not correct, folder can't be created"; - private static Map> stringTypesMap = new TreeMap<>(); - private static Map, String> typesStringMap = new TreeMap<>(); + private static Map> stringTypesMap = new HashMap<>(); + private static Map typesStringMap = new HashMap<>(); static { stringTypesMap.put("int", Integer.class); @@ -28,21 +31,20 @@ public class Database implements TableProvider { stringTypesMap.put("double", Double.class); stringTypesMap.put("boolean", Boolean.class); stringTypesMap.put("String", String.class); - typesStringMap.put(Integer.class, "int"); - typesStringMap.put(Long.class, "long"); - typesStringMap.put(Byte.class, "byte"); - typesStringMap.put(Float.class, "float"); - typesStringMap.put(java.lang.Double.class, "double"); - typesStringMap.put(Boolean.class, "boolean"); - typesStringMap.put(String.class, "String"); - + stringTypesMap.forEach(new BiConsumer>() { + @Override + public void accept(String alias, Class type) { + typesStringMap.put(type, alias); + } + }); } private Path directoryPath; - private Map tables = new TreeMap<>(); + private Map tables = new HashMap<>(); public Database(String directory) throws IOException { + assertArgumentNotNull(directory, "directory"); File root = new File(directory); directoryPath = root.toPath(); File[] tables = root.listFiles(); @@ -67,7 +69,7 @@ public Database(String directory) throws IOException { types.add(type); } } - StoreableTable table = new StoreableTable(tablename, this, types); + StoreableTable table = generateTable(tablename, types); this.tables.put(tablename, table); } else { throw new IOException("Where is not signature file in table " + tablename); @@ -78,24 +80,32 @@ public Database(String directory) throws IOException { } } - protected void fileFoundInRootDirectory(File file) throws IOException { - throw new IOException("There is file " + file.getName() + " in root directory"); + public static void throwIOException(Exception e) throws IOException { + if (e.getCause() != null) { + try { + IOException ioException = (IOException) e.getCause(); + throw ioException; + } catch (ClassCastException wrongClass) { + //Not IOException + } + throw new IOException(e.getMessage(), e); + } } - private void assertNameNotNull(String name) { - if (name == null) { - throw new IllegalArgumentException("Argument \"name\" is null"); - } + protected void fileFoundInRootDirectory(File file) throws IOException { + throw new IOException("There is file " + file.getName() + " in root directory"); } @Override public Table getTable(String name) { - assertNameNotNull(name); + assertArgumentNotNull(name, "name"); return tables.get(name); } @Override public Table createTable(String name, List> columnTypes) throws IOException { + assertArgumentNotNull(name, "name"); + assertArgumentNotNull(columnTypes, "columnTypes"); Table checkExists = getTable(name); if (checkExists == null) { Path rootDirectoryPath = getRootDirectoryPath(); @@ -122,7 +132,7 @@ public Table createTable(String name, List> columnTypes) throws IOExcep } throw ex; } - StoreableTable table = generateTable(name); + StoreableTable table = generateTable(name, columnTypes); tables.put(name, table); return table; } else { @@ -139,7 +149,7 @@ private String createTableSignature(List> columnTypes) { if (column == null) { throw new IllegalArgumentException("Null column"); } - Class type = stringTypesMap.get(column); + String type = typesStringMap.get(column); if (type == null) { throw new IllegalArgumentException("Type is not supported"); } @@ -154,20 +164,29 @@ public Path getRootDirectoryPath() { return directoryPath; } - private StoreableTable generateTable(String name) { + private StoreableTable generateTable(String name, List> types) { return new StoreableTable(name, this, types); } @Override public void removeTable(String name) throws IOException { + assertArgumentNotNull(name, "name"); StoreableTable table = (StoreableTable) getTable(name); - table.drop(); - tables.remove(table); - Files.delete(directoryPath.resolve(name)); + if (table == null) { + throw new IllegalStateException("There is no table with name \"" + name + "\""); + } + try { + table.drop(); + } catch (DatabaseFileStructureException | LoadOrSaveException e) { + throwIOException(e); + } + tables.remove(name); } @Override public Storeable deserialize(Table table, String value) throws ParseException { + assertArgumentNotNull(table, "table"); + assertArgumentNotNull(value, "value"); List data; try { data = (List) JsonParser.parseJson(value); @@ -235,6 +254,8 @@ private boolean tryCastFloat(Table table, int column, Object value, Storeable re @Override public String serialize(Table table, Storeable value) throws ColumnFormatException { + assertArgumentNotNull(table, "table"); + assertArgumentNotNull(value, "value"); List data = new ArrayList<>(); for (int i = 0; i < table.getColumnsCount(); i++) { data.add(value.getColumnAt(i)); @@ -248,12 +269,21 @@ public String serialize(Table table, Storeable value) throws ColumnFormatExcepti @Override public Storeable createFor(Table table) { + assertArgumentNotNull(table, "table"); return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable(Arrays.asList( new Object[table.getColumnsCount()]), table); } @Override public Storeable createFor(Table table, List values) throws ColumnFormatException, IndexOutOfBoundsException { + assertArgumentNotNull(table, "table"); + assertArgumentNotNull(values, "values"); return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable((List) values, table); } + + private void assertArgumentNotNull(Object argument, String name) { + if (argument == null) { + throw new IllegalArgumentException("Argument \"" + name + "\" is null"); + } + } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java index 58e4897b0..52f9bc6e8 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java @@ -6,13 +6,13 @@ public class DatabaseState extends ru.fizteh.fivt.students.LebedevAleksey.MultiF protected Database database; public DatabaseState() { - //TODO - throw new NotImplementedException(); - String directoryPath = System.getProperty("fizteh.db.dir"); - if (directoryPath == null) { - //throw ... - } else { - database = new Database(directoryPath); - } +// //TODO +// throw new NotImplementedException(); +// String directoryPath = System.getProperty("fizteh.db.dir"); +// if (directoryPath == null) { +// //throw ... +// } else { +// database = new Database(directoryPath); +// } } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java index ff4004824..19395d13c 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java @@ -2,129 +2,133 @@ import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.*; +import java.io.IOException; import java.util.ArrayList; import java.util.List; public class Main { - - public static final String USE_COMMAND = "use"; - public static final boolean NO_TABLE_RETURN_CODE = true; - public static final String EXIT_COMMAND = "exit"; - - public static void main(String[] args) { - try { - DatabaseState state = new DatabaseState(); - List commands = new ArrayList<>(MultiFileHashMap.getCommands()); - for (int i = 0; i < commands.size(); i++) { - if (commands.get(i).getName().equals(USE_COMMAND)) { - commands.set(i, new Command(USE_COMMAND, 1) { - @Override - protected boolean action(InterpreterState state, String[] arguments) { - StoreableTable currentTable; - int changesCount = 0; - currentTable = getCurrentTableWithoutMessages(state); - if (currentTable != null) { - changesCount = currentTable.changesCount(); - } - if (changesCount == 0) { - String name = arguments[0]; - try { - MultiFileHashMap.toDatabaseState(state).getDatabase().useTable(name); - System.out.println("using " + name); - } catch (TableNotFoundException ex) { - System.out.println(name + " not exists"); - } catch (DatabaseFileStructureException | LoadOrSaveException e) { - System.err.println(e.getMessage()); - return false; - } - } else { - System.out.println(changesCount + " unsaved changes"); - } - return true; - } - }); - } - if (commands.get(i).getName().equals(EXIT_COMMAND)) { - commands.set(i, new Command(EXIT_COMMAND, 0) { - @Override - protected boolean action(InterpreterState state, String[] arguments) { - StoreableTable currentTable = getCurrentTableWithoutMessages(state); - int changesCount = 0; - if (currentTable != null) { - changesCount = currentTable.changesCount(); - } - if (changesCount == 0) { - state.exit(); - return false; - } else { - System.out.println(changesCount + " unsaved changes"); - } - return true; - } - }); - } - } - commands.add(new Command("commit", 0) { - @Override - protected boolean action(InterpreterState state, String[] arguments) { - try { - System.out.println(getCurrentTable(state).commit()); - return true; - } catch (DatabaseFileStructureException | LoadOrSaveException e) { - System.err.println(e.getMessage()); - return false; - } catch (TableNotFoundException e) { - return true; - } - } - }); - commands.add(new Command("rollback", 0) { - @Override - protected boolean action(InterpreterState state, String[] arguments) { - try { - System.out.println(getCurrentTable(state).rollback()); - } catch (TableNotFoundException e) { - return true; - } - return NO_TABLE_RETURN_CODE; - } - }); - commands.add(new Command("size", 0) { - @Override - protected boolean action(InterpreterState state, String[] arguments) { - try { - System.out.println(getCurrentTable(state).count()); - return true; - } catch (LoadOrSaveException | DatabaseFileStructureException e) { - System.err.println(e.getMessage()); - return false; - } catch (TableNotFoundException e) { - return NO_TABLE_RETURN_CODE; - } - } - }); - runDatabaseInterpreter(args, state, commands); - } catch (DatabaseFileStructureException | LoadOrSaveException ex) { - System.err.println(ex.getMessage()); - System.exit(4); - } - } - - private static StoreableTable getCurrentTableWithoutMessages(InterpreterState state) { - return (StoreableTable) ((DatabaseState) state).getDatabase().getCurrentTable(); - } - - private static StoreableTable getCurrentTable(InterpreterState state) throws TableNotFoundException { - return ((StoreableTable) MultiFileHashMap.getCurrentTable(state)); - } - - private static void runDatabaseInterpreter(String[] args, DatabaseState state, List commands) { - Interpreter interpreter = new Interpreter(commands, state); - interpreter.run(args); - if (args.length != 0) { - if (!state.tryToSave()) { - System.exit(2); - } - } + public static void main(String[] args) throws IOException { + Database db=new Database(""); } +// +// public static final String USE_COMMAND = "use"; +// public static final boolean NO_TABLE_RETURN_CODE = true; +// public static final String EXIT_COMMAND = "exit"; +// +// public static void main(String[] args) { +// try { +// DatabaseState state = new DatabaseState(); +// List commands = new ArrayList<>(MultiFileHashMap.getCommands()); +// for (int i = 0; i < commands.size(); i++) { +// if (commands.get(i).getName().equals(USE_COMMAND)) { +// commands.set(i, new Command(USE_COMMAND, 1) { +// @Override +// protected boolean action(InterpreterState state, String[] arguments) { +// StoreableTable currentTable; +// int changesCount = 0; +// currentTable = getCurrentTableWithoutMessages(state); +// if (currentTable != null) { +// changesCount = currentTable.changesCount(); +// } +// if (changesCount == 0) { +// String name = arguments[0]; +// try { +// MultiFileHashMap.toDatabaseState(state).getDatabase().useTable(name); +// System.out.println("using " + name); +// } catch (TableNotFoundException ex) { +// System.out.println(name + " not exists"); +// } catch (DatabaseFileStructureException | LoadOrSaveException e) { +// System.err.println(e.getMessage()); +// return false; +// } +// } else { +// System.out.println(changesCount + " unsaved changes"); +// } +// return true; +// } +// }); +// } +// if (commands.get(i).getName().equals(EXIT_COMMAND)) { +// commands.set(i, new Command(EXIT_COMMAND, 0) { +// @Override +// protected boolean action(InterpreterState state, String[] arguments) { +// StoreableTable currentTable = getCurrentTableWithoutMessages(state); +// int changesCount = 0; +// if (currentTable != null) { +// changesCount = currentTable.changesCount(); +// } +// if (changesCount == 0) { +// state.exit(); +// return false; +// } else { +// System.out.println(changesCount + " unsaved changes"); +// } +// return true; +// } +// }); +// } +// } +// commands.add(new Command("commit", 0) { +// @Override +// protected boolean action(InterpreterState state, String[] arguments) { +// try { +// System.out.println(getCurrentTable(state).commit()); +// return true; +// } catch (DatabaseFileStructureException | LoadOrSaveException e) { +// System.err.println(e.getMessage()); +// return false; +// } catch (TableNotFoundException e) { +// return true; +// } +// } +// }); +// commands.add(new Command("rollback", 0) { +// @Override +// protected boolean action(InterpreterState state, String[] arguments) { +// try { +// System.out.println(getCurrentTable(state).rollback()); +// } catch (TableNotFoundException e) { +// return true; +// } +// return NO_TABLE_RETURN_CODE; +// } +// }); +// commands.add(new Command("size", 0) { +// @Override +// protected boolean action(InterpreterState state, String[] arguments) { +// try { +// System.out.println(getCurrentTable(state).count()); +// return true; +// } catch (LoadOrSaveException | DatabaseFileStructureException e) { +// System.err.println(e.getMessage()); +// return false; +// } catch (TableNotFoundException e) { +// return NO_TABLE_RETURN_CODE; +// } +// } +// }); +// runDatabaseInterpreter(args, state, commands); +// } catch (DatabaseFileStructureException | LoadOrSaveException ex) { +// System.err.println(ex.getMessage()); +// System.exit(4); +// } +// } +// +// private static StoreableTable getCurrentTableWithoutMessages(InterpreterState state) { +// return (StoreableTable) ((DatabaseState) state).getDatabase().getCurrentTable(); +// } +// +// private static StoreableTable getCurrentTable(InterpreterState state) throws TableNotFoundException { +// return ((StoreableTable) MultiFileHashMap.getCurrentTable(state)); +// } +// +// private static void runDatabaseInterpreter(String[] args, DatabaseState state, List commands) { +// Interpreter interpreter = new Interpreter(commands, state); +// interpreter.run(args); +// if (args.length != 0) { +// if (!state.tryToSave()) { +// System.exit(2); +// } +// } +// } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java index 3c885f9a6..b1b288e23 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java @@ -8,10 +8,10 @@ import ru.fizteh.fivt.students.LebedevAleksey.junit.DatabaseException; import java.io.IOException; +import java.nio.file.Path; import java.text.ParseException; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; +import java.util.*; +import java.util.function.Consumer; public class StoreableTable implements ru.fizteh.fivt.storage.structured.Table { private final String name; @@ -24,7 +24,7 @@ public StoreableTable(String name, Database databaseParent, List> types this.name = name; database = databaseParent; columnTypes = types; - Table stringTable = new Table(name, databaseParent.getRootDirectoryPath()); + stringTable = new Table(name, databaseParent.getRootDirectoryPath()); } private void checkKeyNotNull(String key) { @@ -62,7 +62,12 @@ private String getString(String key) throws LoadOrSaveException, DatabaseFileStr public Storeable put(String key, Storeable value) throws ColumnFormatException { checkKeyValueNotNull(key, value); try { - return database.deserialize(this, putStrings(key, database.serialize(this, value))); + String result = putStrings(key, database.serialize(this, value)); + if (result == null) { + return null; + } else { + return database.deserialize(this, result); + } } catch (DatabaseFileStructureException | LoadOrSaveException e) { throw new DatabaseException(e); } catch (ParseException e) { @@ -126,9 +131,9 @@ public int commit() throws IOException { changedKeys.clear(); stringTable.save(); } catch (DatabaseFileStructureException | LoadOrSaveException e) { - throw new IOException(e.getMessage(), e); + Database.throwIOException(e); } - return 0; + return changes; } public int changesCount() { @@ -151,7 +156,12 @@ public String getName() { public Storeable get(String key) { checkKeyNotNull(key); try { - return database.deserialize(this, getString(key)); + String result = getString(key); + if (result == null) { + return null; + } else { + return database.deserialize(this, result); + } } catch (ParseException | LoadOrSaveException | DatabaseFileStructureException e) { throw new DatabaseException(e); } @@ -168,6 +178,36 @@ public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException } public void drop() throws DatabaseFileStructureException, LoadOrSaveException { + Path signatureFile = database.getRootDirectoryPath().resolve(getName()). + resolve(Database.TABLE_SIGNATURE_FILE_NAME); + if (!signatureFile.toFile().delete()) { + throw new LoadOrSaveException("Can't delete signature file for table " + getName()); + } stringTable.drop(); } + + public List list() throws IOException { + try { + Set items = new TreeSet<>(stringTable.list()); + for (String key : changedKeys.keySet()) { + String value = changedKeys.get(key); + if (value == null) { + items.remove(key); + } else { + items.add(key); + } + } + final ArrayList result = new ArrayList<>(items.size()); + items.forEach(new Consumer() { + @Override + public void accept(String s) { + result.add(s); + } + }); + return result; + } catch (DatabaseFileStructureException | LoadOrSaveException e) { + Database.throwIOException(e); + return null; // unreached + } + } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTestWithWrapper.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTestWithWrapper.java new file mode 100644 index 000000000..198b79cfb --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTestWithWrapper.java @@ -0,0 +1,151 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.strings.Table; +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.students.LebedevAleksey.junit.AdditionalAssert; +import ru.fizteh.fivt.students.LebedevAleksey.junit.DatabaseException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.Database; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.DatabaseFactory; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +public class DatabaseTestWithWrapper { + @ClassRule + public static TemporaryFolder folder = new TemporaryFolder(); + private static File dbPath; + private static TableProvider database; + + @BeforeClass + public static void setUpClass() throws IOException { + dbPath = folder.newFolder("db"); + database = getTableProviderWrapper(); + } + + protected static TableProviderWrapper getTableProviderWrapper() throws IOException { + return new TableProviderWrapper((Database) new DatabaseFactory().create(dbPath.getPath())); + } + + @Test + public void testGetAndCreateTable() { + Assert.assertNull(database.getTable("abc")); + Table table = database.createTable("abc"); + table.put("a", "a"); + table.commit(); + table = database.getTable("abc"); + Assert.assertEquals(1, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a"}, table.list()); + Assert.assertEquals(null, database.getTable("abcd")); + Assert.assertEquals(null, database.getTable("ab")); + Assert.assertEquals(null, database.getTable("a")); + Assert.assertEquals(null, database.getTable("qwerty")); + database.removeTable("abc"); + } + + @Test(expected = IllegalStateException.class) + public void testExceptionThrowsThanDropNotExistingTable() { + database.removeTable("notExist"); + } + + @Test + public void testCanRemoveTable() { + Table table = database.createTable("name"); + table.put("a", "a"); + table.commit(); + database.removeTable("name"); + Assert.assertEquals(null, database.getTable("name")); + } + + @Test + public void testCanRemoveTableWithNoCommits() { + Table table = database.createTable("name"); + database.removeTable("name"); + Assert.assertEquals(null, database.getTable("name")); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateTableThrowsExceptionThanArgumentIsNull() { + database.createTable(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testRemoveTableThrowsExceptionThanArgumentIsNull() { + database.removeTable(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetTableThrowsExceptionThanArgumentIsNull() { + database.getTable(null); + } + + @Test + public void testReturnNullWhenTableExist() { + database.createTable("qwerty"); + Assert.assertEquals(null, database.createTable("qwerty")); + database.removeTable("qwerty"); + } + + @Test + public void testGetTableFromManyTablesAndLoadDatabase() throws IOException { + for (int i = 0; i < 10; i++) { + database.createTable("t" + i); + } + for (int i = 0; i < 10; i++) { + assertCanGetTableTNum(i); + } + database = getTableProviderWrapper(); + for (int i = 0; i < 10; i++) { + database.removeTable("t" + i); + for (int j = 0; j <= i; j++) { + Assert.assertEquals(null, database.getTable("t" + j)); + } + for (int j = i + 1; j < 10; j++) { + assertCanGetTableTNum(j); + } + } + } + + + private void assertCanGetTableTNum(int i) { + Assert.assertEquals("t" + i, database.getTable("t" + i).getName()); + } +} + +class TableProviderWrapper implements ru.fizteh.fivt.storage.strings.TableProvider { + private Database database; + + public TableProviderWrapper(Database tableProvider) { + database = tableProvider; + } + + @Override + public Table getTable(String name) { + return StringTableWrapper.create(database.getTable(name), database); + } + + @Override + public Table createTable(String name) { + try { + return StringTableWrapper.create(database.createTable(name, Arrays.asList(String.class)), database); + } catch (IOException e) { + throw new DatabaseException(e); + } + + } + + @Override + public void removeTable(String name) { + try { + database.removeTable(name); + } catch (IOException e) { + throw new DatabaseException(e); + } + } +} + diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java new file mode 100644 index 000000000..39f411542 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java @@ -0,0 +1,468 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.*; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.strings.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.students.LebedevAleksey.junit.AdditionalAssert; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.Database; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.DatabaseFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; + +public class StringTableTest { + public static final String TEST_TABLE_NAME = "TestTable"; + @ClassRule + public static TemporaryFolder folder = new TemporaryFolder(); + private static File dbPath; + private static TableProvider database; + private Table table; + + @BeforeClass + public static void setUpClass() throws IOException { + dbPath = folder.newFolder("db"); + database = new DatabaseFactory().create(dbPath.getPath()); + } + + @Before + public void setUp() throws Exception { + ru.fizteh.fivt.storage.structured.Table tableInstance = database.getTable(TEST_TABLE_NAME); + if (tableInstance != null) { + database.removeTable(TEST_TABLE_NAME); + } + createTable(); + } + + protected void createTable() throws IOException { + table = StringTableWrapper.create(database.createTable(TEST_TABLE_NAME, Arrays.asList(String.class)), + (Database) database); + } + + @After + public void tearDown() throws Exception { + database.removeTable(TEST_TABLE_NAME); + } + + @Test + public void testGetName() throws Exception { + Assert.assertEquals(TEST_TABLE_NAME, table.getName()); + Assert.assertEquals("Qwerty", + database.createTable("Qwerty", Arrays.asList(Integer.class, Boolean.class)).getName()); + database.removeTable("Qwerty"); + } + + @Test + public void testGetAndPutCommands() throws Exception { + Assert.assertEquals(null, table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals(null, table.get("c")); + Assert.assertEquals(null, table.get("d")); + + Assert.assertEquals(0, table.list().size()); + Assert.assertEquals(null, table.put("a", "b")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a"}, table.list()); + + Assert.assertEquals("b", table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals(null, table.get("c")); + Assert.assertEquals(null, table.get("d")); + + AdditionalAssert.assertArrayAndListEquals(new String[]{"a"}, table.list()); + Assert.assertEquals(null, table.put("c", "d")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + + Assert.assertEquals("b", table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals("d", table.get("c")); + Assert.assertEquals(null, table.get("d")); + + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + Assert.assertEquals("b", table.put("a", "d")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + + Assert.assertEquals("d", table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals("d", table.get("c")); + Assert.assertEquals(null, table.get("d")); + + Assert.assertEquals(null, table.get("qwerty")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + Assert.assertEquals(null, table.put("qwerty", "asdfg")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c", "qwerty"}, table.list()); + Assert.assertEquals("asdfg", table.get("qwerty")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c", "qwerty"}, table.list()); + Assert.assertEquals("asdfg", table.put("qwerty", "test")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c", "qwerty"}, table.list()); + Assert.assertEquals("test", table.get("qwerty")); + } + + @Test + public void testRemoveAndListAndSizeCommands() throws Exception { + Assert.assertEquals(0, table.list().size()); + Assert.assertEquals(0, table.size()); + + table.put("a", "ab"); + table.put("b", "bb"); + table.put("c", "cb"); + + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "b", "c"}, table.list()); + + Assert.assertEquals(null, table.remove("d")); + + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "b", "c"}, table.list()); + + Assert.assertEquals("bb", table.remove("b")); + + Assert.assertEquals(2, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c"}, table.list()); + + table.put("name", "surname"); + + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c", "name"}, table.list()); + + Assert.assertEquals("cb", table.remove("c")); + + Assert.assertEquals(2, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "name"}, table.list()); + Assert.assertEquals("ab", table.get("a")); + Assert.assertEquals("surname", table.get("name")); + + Assert.assertEquals("surname", table.remove("name")); + + Assert.assertEquals(1, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a"}, table.list()); + Assert.assertEquals("ab", table.get("a")); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetIncorrectArgument() { + table.get(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testRemoveIncorrectArgument() { + table.remove(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testPutFirstIncorrectArgument() { + table.put(null, "a"); + } + + @Test(expected = IllegalArgumentException.class) + public void testPutSecondIncorrectArgument() { + table.put("a", null); + } + + @Test(expected = IllegalArgumentException.class) + public void testPutBothIncorrectArguments() { + table.put(null, null); + } + + @Test + public void testIncorrectPutDoesntChangeAnything() { + checkEmpty(); + try { + table.put("a", null); + Assert.fail("Should be exception"); + } catch (IllegalArgumentException e) { + //We are waiting this exception + } + checkEmpty(); + try { + table.put(null, "a"); + Assert.fail("Should be exception"); + } catch (IllegalArgumentException e) { + //We are waiting this exception + } + checkEmpty(); + try { + table.put(null, null); + Assert.fail("Should be exception"); + } catch (IllegalArgumentException e) { + //We are waiting this exception + } + checkEmpty(); + try { + table.get(null); + Assert.fail("Should be exception"); + } catch (IllegalArgumentException e) { + //We are waiting this exception + } + checkEmpty(); + try { + table.remove(null); + Assert.fail("Should be exception"); + } catch (IllegalArgumentException e) { + //We are waiting this exception + } + checkEmpty(); + table.put("a", "a"); + Assert.assertEquals(1, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a"}, table.list()); + Assert.assertEquals("a", table.get("a")); + } + + private void checkEmpty() { + Assert.assertEquals(0, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[0], table.list()); + Assert.assertEquals(null, table.get("a")); + } + + private void make1Change1() { + table.put("a", "a"); + } + + private void make1ChangeRemove() { + table.remove("a"); + } + + private void make2Change() { + table.put("a", "a"); + table.put("b", "b"); + table.put("c", "c"); + table.remove("c"); + } + + + private void make3Change() { + table.put("a", "ab"); + table.put("b", "bb"); + table.put("c", "cb"); + } + + private void make4Change() { + table.put("a", "a"); + table.remove("c"); + table.put("qwerty", "1234567"); + table.put("qw", "12"); + } + + @Test + public void testCommitCounts() throws Exception { + make1Change1(); + Assert.assertEquals(1, table.commit()); + Assert.assertEquals(0, table.commit()); + make1ChangeRemove(); + Assert.assertEquals(1, table.commit()); + Assert.assertEquals(0, table.commit()); + make2Change(); + Assert.assertEquals(2, table.rollback()); + make2Change(); + Assert.assertEquals(2, table.commit()); + make3Change(); + Assert.assertEquals(3, table.rollback()); + make3Change(); + Assert.assertEquals(3, table.commit()); + make3Change(); + Assert.assertEquals(0, table.rollback()); + make4Change(); + Assert.assertEquals(4, table.rollback()); + } + + @Test + public void testRollbackCount() throws Exception { + make1Change1(); + Assert.assertEquals(1, table.rollback()); + Assert.assertEquals(0, table.rollback()); + make1ChangeRemove(); + Assert.assertEquals(0, table.rollback()); + Assert.assertEquals(0, table.rollback()); + make2Change(); + Assert.assertEquals(2, table.commit()); + make3Change(); + Assert.assertEquals(3, table.commit()); + make3Change(); + Assert.assertEquals(0, table.commit()); + make4Change(); + Assert.assertEquals(4, table.commit()); + } + + private boolean hasFlag(int position, int value) { + return (((value >> position) % 2) == 1); + } + + + @Test + public void testCommit() throws IOException { + for (int i = 0; i < 1 << 5; i++) { + database.removeTable(TEST_TABLE_NAME); + createTable(); + Assert.assertEquals(null, table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals(null, table.get("c")); + Assert.assertEquals(null, table.get("d")); + + Assert.assertEquals(0, table.list().size()); + Assert.assertEquals(null, table.put("a", "b")); + checkCommit(i, 0); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a"}, table.list()); + + Assert.assertEquals("b", table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals(null, table.get("c")); + Assert.assertEquals(null, table.get("d")); + + AdditionalAssert.assertArrayAndListEquals(new String[]{"a"}, table.list()); + Assert.assertEquals(null, table.put("c", "d")); + checkCommit(i, 1); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + + Assert.assertEquals("b", table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals("d", table.get("c")); + Assert.assertEquals(null, table.get("d")); + + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + Assert.assertEquals("b", table.put("a", "d")); + checkCommit(i, 2); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + + Assert.assertEquals("d", table.get("a")); + Assert.assertEquals(null, table.get("b")); + Assert.assertEquals("d", table.get("c")); + Assert.assertEquals(null, table.get("d")); + + Assert.assertEquals(null, table.get("qwerty")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c"}, table.list()); + Assert.assertEquals(null, table.put("qwerty", "asdfg")); + checkCommit(i, 3); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c", "qwerty"}, table.list()); + Assert.assertEquals("asdfg", table.get("qwerty")); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c", "qwerty"}, table.list()); + Assert.assertEquals("asdfg", table.put("qwerty", "test")); + checkCommit(i, 4); + AdditionalAssert.assertArrayAndListEquals(new String[]{"a", "c", "qwerty"}, table.list()); + Assert.assertEquals("test", table.get("qwerty")); + } + for (int i = 0; i < 1 << 6; i++) { + database.removeTable(TEST_TABLE_NAME); + createTable(); + + Assert.assertEquals(0, table.list().size()); + Assert.assertEquals(0, table.size()); + table.put("a", "ab"); + table.put("b", "bb"); + table.put("c", "cb"); + checkCommit(i, 0); + + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "b", "c"}, table.list()); + + Assert.assertEquals(null, table.remove("d")); + checkCommit(i, 1); + + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "b", "c"}, table.list()); + + Assert.assertEquals("bb", table.remove("b")); + checkCommit(i, 2); + + Assert.assertEquals(2, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c"}, table.list()); + + table.put("name", "surname"); + checkCommit(i, 3); + + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c", "name"}, table.list()); + + Assert.assertEquals("cb", table.remove("c")); + checkCommit(i, 4); + + Assert.assertEquals(2, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "name"}, table.list()); + Assert.assertEquals("ab", table.get("a")); + Assert.assertEquals("surname", table.get("name")); + + Assert.assertEquals("surname", table.remove("name")); + checkCommit(i, 5); + + Assert.assertEquals(1, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a"}, table.list()); + Assert.assertEquals("ab", table.get("a")); + } + } + + private void checkCommit(int value, int position) throws IOException { + if (hasFlag(position, value)) { + checkCommit(); + } + } + + private int checkCommit() throws IOException { + int changes = table.commit(); + createTable(); + Path tablePath = dbPath.toPath().resolve(TEST_TABLE_NAME); + File[] directories = tablePath.toFile().listFiles(); + for (File dir : directories) { + if (dir.isDirectory()) { + File[] files = dir.listFiles(); + Assert.assertTrue(files.length > 0); + for (File file : files) { + Assert.assertTrue(file.length() > 0); + } + } else { + Assert.assertEquals("signature.tsv", dir.getName()); + } + } + return changes; + } + + @Test + public void testCommitWithCollisions() throws IOException { + table.put("Q1", "text"); + table.put("r2", "text"); + Assert.assertEquals(2, checkCommit()); + Assert.assertEquals(2, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"Q1", "r2"}, table.list()); + table.remove("Q1"); + table.put("test", "text"); + Assert.assertEquals(2, checkCommit()); + Assert.assertEquals(2, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"test", "r2"}, table.list()); + table.put("Q1", "text"); + table.put("r2", "text"); + Assert.assertEquals(1, checkCommit()); + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"Q1", "r2", "test"}, table.list()); + table.remove("Q1"); + table.remove("r2"); + Assert.assertEquals(2, checkCommit()); + Assert.assertEquals(1, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"test"}, table.list()); + } + + @Test + public void testRollback() { + table.put("a", "a"); + table.put("b", "b"); + table.put("c", "c"); + table.commit(); + Assert.assertEquals("a", table.get("a")); + Assert.assertEquals("b", table.get("b")); + Assert.assertEquals("c", table.get("c")); + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c", "b"}, table.list()); + table.put("a", "e"); + table.remove("b"); + table.put("d", "d"); + Assert.assertEquals("e", table.get("a")); + Assert.assertEquals("c", table.get("c")); + Assert.assertEquals("d", table.get("d")); + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c", "d"}, table.list()); + table.rollback(); + Assert.assertEquals("a", table.get("a")); + Assert.assertEquals("b", table.get("b")); + Assert.assertEquals("c", table.get("c")); + Assert.assertEquals(3, table.size()); + AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c", "b"}, table.list()); + } +} \ No newline at end of file diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java new file mode 100644 index 000000000..8992fc436 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java @@ -0,0 +1,91 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + + +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.students.LebedevAleksey.junit.DatabaseException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.Database; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.StoreableTable; + +import java.io.IOException; +import java.text.ParseException; +import java.util.List; + +public class StringTableWrapper implements ru.fizteh.fivt.storage.strings.Table { + + private StoreableTable table; + private Database database; + + private StringTableWrapper(StoreableTable table, Database database) { + this.table = table; + this.database = database; + } + + public static StringTableWrapper create(ru.fizteh.fivt.storage.structured.Table table, Database database) { + if (table == null) { + return null; + } else { + return new StringTableWrapper((StoreableTable) table, database); + } + } + + + @Override + public String getName() { + return table.getName(); + } + + @Override + public String get(String key) { + Storeable storeable = table.get(key); + return (storeable == null ? null : (String) storeable.getColumnAt(0)); + } + + @Override + public String put(String key, String value) { + try { + Storeable result; + if (value == null) { + result = table.put(key, null); + } else { + result = table.put(key, database.deserialize(table, "[\"" + value + "\"]")); + } + return result == null ? null : (String) result.getColumnAt(0); + } catch (ParseException e) { + throw new DatabaseException(e); + } + } + + @Override + public String remove(String key) { + Storeable result = table.remove(key); + return result == null ? null : (String) result.getColumnAt(0); + } + + @Override + public int size() { + return table.size(); + } + + @Override + public int commit() { + try { + return table.commit(); + } catch (IOException e) { + throw new DatabaseException(e); + } + } + + @Override + public int rollback() { + return table.rollback(); + } + + @Override + public List list() { + try { + return table.list(); + } catch (IOException e) { + throw new DatabaseException(e); + } + } +} From aa28b2ec1ed21a54248f7dbbb779c3b1c3581761 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Mon, 8 Dec 2014 17:21:30 +0300 Subject: [PATCH 21/36] Fix test bug --- .../LebedevAleksey/storeable/tests/StringTableTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java index 39f411542..95967c5bb 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java @@ -41,6 +41,13 @@ protected void createTable() throws IOException { (Database) database); } + + protected void getTable() throws IOException { + database = new DatabaseFactory().create(dbPath.getPath()); + table = StringTableWrapper.create(database.getTable(TEST_TABLE_NAME), + (Database) database); + } + @After public void tearDown() throws Exception { database.removeTable(TEST_TABLE_NAME); @@ -398,7 +405,7 @@ private void checkCommit(int value, int position) throws IOException { private int checkCommit() throws IOException { int changes = table.commit(); - createTable(); + getTable(); Path tablePath = dbPath.toPath().resolve(TEST_TABLE_NAME); File[] directories = tablePath.toFile().listFiles(); for (File dir : directories) { From 59d0e872af738fa06851d16abb616c3a77da7a46 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sun, 14 Dec 2014 23:23:53 +0300 Subject: [PATCH 22/36] Interpreter and tests for command --- .../LebedevAleksey/storeable/Database.java | 34 +- .../storeable/DatabaseState.java | 43 +- .../LebedevAleksey/storeable/Main.java | 419 +++++++++++++----- .../interpreter/ArgumentException.java | 11 + .../storeable/interpreter/Command.java | 37 ++ .../storeable/interpreter/Interpreter.java | 302 +++++++++++++ .../interpreter/InterpreterState.java | 13 + .../storeable/interpreter/ParsedCommand.java | 22 + .../interpreter/ParserException.java | 11 + .../interpreter/StreamsContainer.java | 36 ++ .../storeable/tests/CommandTest.java | 214 +++++++++ .../storeable/tests/DatabaseTest.java | 126 ++++++ .../storeable/tests/InterpreterTest.java | 265 +++++++++++ 13 files changed, 1394 insertions(+), 139 deletions(-) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ArgumentException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Command.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/InterpreterState.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParsedCommand.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParserException.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index 498e71399..42144ec04 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -6,6 +6,8 @@ import ru.fizteh.fivt.storage.structured.TableProvider; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.LoadOrSaveException; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Pair; +import ru.fizteh.fivt.students.LebedevAleksey.junit.DatabaseException; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.BrokenJsonException; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonParser; import ru.fizteh.fivt.students.LebedevAleksey.storeable.json.JsonUnsupportedObjectException; @@ -80,7 +82,7 @@ public Database(String directory) throws IOException { } } - public static void throwIOException(Exception e) throws IOException { + public static void throwIOException(Throwable e) throws IOException { if (e.getCause() != null) { try { IOException ioException = (IOException) e.getCause(); @@ -88,8 +90,21 @@ public static void throwIOException(Exception e) throws IOException { } catch (ClassCastException wrongClass) { //Not IOException } - throw new IOException(e.getMessage(), e); } + throw new IOException(e.getMessage(), e); + } + + public static List> ParseTypes(String[] input) throws ParseException { + List> types = new ArrayList<>(input.length); + for (String item : input) { + Class type = stringTypesMap.get(item); + if (type != null) { + types.add(type); + } else { + throw new ParseException("Wrong type: " + item, -1); + } + } + return types; } protected void fileFoundInRootDirectory(File file) throws IOException { @@ -286,4 +301,19 @@ private void assertArgumentNotNull(Object argument, String name) { throw new IllegalArgumentException("Argument \"" + name + "\" is null"); } } + + public List> listTables() throws IOException { + final List> result = new ArrayList<>(tables.size()); + try { + tables.forEach(new BiConsumer() { + @Override + public void accept(String s, StoreableTable table) { + result.add(new Pair(s, table.size())); + } + }); + } catch (DatabaseException e) { + throwIOException(e.getCause()); + } + return result; + } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java index 52f9bc6e8..c2718b905 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/DatabaseState.java @@ -1,18 +1,33 @@ package ru.fizteh.fivt.students.LebedevAleksey.storeable; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; - -public class DatabaseState extends ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.InterpreterState { - protected Database database; - - public DatabaseState() { -// //TODO -// throw new NotImplementedException(); -// String directoryPath = System.getProperty("fizteh.db.dir"); -// if (directoryPath == null) { -// //throw ... -// } else { -// database = new Database(directoryPath); -// } +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.InterpreterState; + +import java.io.IOException; + +public class DatabaseState extends InterpreterState { + private Database database; + private StoreableTable table; + + public StoreableTable getCurrentTable() { + return table; + } + + public void setCurrentTable(StoreableTable table) { + this.table = table; + } + + public Database getDatabase() { + return database; + } + + + public DatabaseState() throws IOException, DatabaseFileStructureException { + String directoryPath = System.getProperty("fizteh.db.dir"); + if (directoryPath == null) { + throw new DatabaseFileStructureException("Database directory doesn't set"); + } else { + database = new Database(directoryPath); + } } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java index 19395d13c..4c387d535 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java @@ -1,134 +1,307 @@ package ru.fizteh.fivt.students.LebedevAleksey.storeable; -import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.*; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Pair; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.TableNotFoundException; +import ru.fizteh.fivt.students.LebedevAleksey.junit.DatabaseException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.*; import java.io.IOException; +import java.text.ParseException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) throws IOException { - Database db=new Database(""); + runInterpreter(args, getCommands()); + } + + public static void runInterpreter(String[] args, List commands) { + try { + DatabaseState state = new DatabaseState(); + Interpreter interpreter = new Interpreter(commands, state, new StreamsContainer()); + interpreter.run(args); + } catch (DatabaseFileStructureException | IOException ex) { + System.err.println(ex.getMessage()); + System.exit(3); + } + } + + public static List getCommands() { + List commands = Arrays.asList(new Command[]{new Command("exit", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + StoreableTable currentTable = toDatabaseState(state).getCurrentTable(); + int changesCount = 0; + if (currentTable != null) { + changesCount = currentTable.changesCount(); + } + if (changesCount == 0) { + state.exit(); + return false; + } else { + streams.getOut().println(changesCount + " unsaved changes"); + } + return true; + } + }, new Command("show", 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + if (arguments[0].equals("tables")) { + List> tables; + try { + tables = toDatabaseState(state).getDatabase().listTables(); + } catch (IOException e) { + streams.getErr().println(e.getMessage()); + return false; + } + for (Pair table : tables) { + streams.getOut().println(table.getKey() + " " + table.getValue()); + } + return true; + } else { + throw new ArgumentException("Unknown argument in show command"); + } + } + }, new Command("create") { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + if (arguments.length >= 2) { + if (arguments[1].startsWith("(") && arguments[arguments.length - 1].endsWith(")")) { + String name = arguments[0]; + try { + String[] types = Arrays.copyOfRange(arguments, 1, arguments.length); + types[0] = types[0].substring(1); + String lastType = types[types.length - 1]; + types[types.length - 1] = lastType.substring(0, lastType.length() - 1); + if (types.length == 1 && types[0].equals("")) { + types = new String[0]; + } + if (toDatabaseState(state).getDatabase(). + createTable(name, Database.ParseTypes(types)) == null) { + streams.getOut().println(name + " exists"); + return true; + } + streams.getOut().println("created"); + return true; + } catch (IOException | DatabaseException e) { + streams.getErr().println(e.getMessage()); + return false; + } catch (ParseException e) { + streams.getErr().println("wrong type (" + e.getMessage() + ")"); + return false; + } + } else { + throw new ArgumentException("There is no correct information about columns"); + } + } else { + throw new ArgumentException("Not enough arguments"); + } + } + }, new Command("drop", 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + String name = arguments[0]; + try { + DatabaseState dbState = toDatabaseState(state); + if (dbState.getCurrentTable() != null && dbState.getCurrentTable().getName().equals(name)) { + dbState.setCurrentTable(null); + } + dbState.getDatabase().removeTable(name); + streams.getOut().println("dropped"); + } catch (IllegalStateException ex) { + streams.getOut().println(name + " not exists"); + return false; + } catch (IOException e) { + streams.getErr().println(e.getMessage()); + return false; + } + return true; + } + }, new Command("use", 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + StoreableTable currentTable; + int changesCount = 0; + currentTable = toDatabaseState(state).getCurrentTable(); + if (currentTable != null) { + changesCount = currentTable.changesCount(); + } + if (changesCount == 0) { + String name = arguments[0]; + Table table = toDatabaseState(state).getDatabase().getTable(name); + if (table == null) { + streams.getOut().println(name + " not exists"); + } else { + streams.getOut().println("using " + name); + toDatabaseState(state).setCurrentTable((StoreableTable) table); + } + } else { + streams.getOut().println(changesCount + " unsaved changes"); + } + return true; + } + }}); + List array = new ArrayList<>(); + array.addAll(commands); + array.addAll(getTableCommands()); + return array; + } + + public static Table getCurrentTable(InterpreterState state) throws TableNotFoundException { + Table table = toDatabaseState(state).getCurrentTable(); + if (table == null) { + System.out.println("no table"); + throw new TableNotFoundException("No table selected"); + } else { + return table; + } + } + + public static DatabaseState toDatabaseState(InterpreterState state) { + return (DatabaseState) state; + } + + private static List getTableCommands() { + return Arrays.asList(new Command("list", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + try { + List result; + result = ((StoreableTable) getCurrentTable(state)).list(); + for (int i = 0; i < result.size(); i++) { + if (i > 0) { + streams.getOut().print(", "); + } + streams.getOut().print(result.get(i)); + } + streams.getOut().println(); + return true; + } catch (IOException | DatabaseException e) { + streams.getErr().println(e.getMessage()); + return false; + } catch (TableNotFoundException e) { + return true; + } + } + }, new Command("put", 2) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + try { + Database database = toDatabaseState(state).getDatabase(); + Table table = getCurrentTable(state); + Storeable result = table.put(arguments[0], database.deserialize(table, arguments[1])); + if (result == null) { + streams.getOut().println("new"); + } else { + streams.getOut().println("overwrite"); + streams.getOut().println(database.serialize(table, result)); + } + return true; + } catch (TableNotFoundException e) { + return true; + } catch (DatabaseException e) { + streams.getErr().println(e.getMessage()); + return false; + } catch (ParseException e) { + streams.getErr().println("wrong type (" + e.getMessage() + ")"); + return false; + } + + } + }, new Command("get", 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + try { + Database database = toDatabaseState(state).getDatabase(); + Table table = getCurrentTable(state); + Storeable result = table.get(arguments[0]); + if (result == null) { + streams.getOut().println("not found"); + } else { + streams.getOut().println("found"); + streams.getOut().println(database.serialize(table, result)); + } + return true; + } catch (DatabaseException e) { + System.err.println(e.getMessage()); + return false; + } catch (TableNotFoundException e) { + return true; + } + } + }, new Command("remove", 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + try { + Table table = getCurrentTable(state); + if (table.remove(arguments[0]) != null) { + streams.getOut().println("removed"); + } else { + streams.getOut().println("not found"); + } + return true; + } catch (TableNotFoundException e) { + return true; + } catch (DatabaseException e) { + streams.getErr().println(e.getMessage()); + return false; + } + } + }, new Command("commit", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + try { + streams.getOut().println(getCurrentTable(state).commit()); + return true; + } catch (IOException | DatabaseException e) { + streams.getErr().println(e.getMessage()); + return false; + } catch (TableNotFoundException e) { + return true; + } + } + }, new Command("rollback", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + try { + streams.getOut().println(getCurrentTable(state).rollback()); + return true; + } catch (DatabaseException e) { + streams.getErr().println(e.getMessage()); + return false; + } catch (TableNotFoundException e) { + return true; + } + } + }, new Command("size", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + try { + streams.getOut().println(getCurrentTable(state).size()); + return true; + } catch (DatabaseException e) { + streams.getErr().println(e.getMessage()); + return false; + } catch (TableNotFoundException e) { + return false; + } + } + }); } -// -// public static final String USE_COMMAND = "use"; -// public static final boolean NO_TABLE_RETURN_CODE = true; -// public static final String EXIT_COMMAND = "exit"; -// -// public static void main(String[] args) { -// try { -// DatabaseState state = new DatabaseState(); -// List commands = new ArrayList<>(MultiFileHashMap.getCommands()); -// for (int i = 0; i < commands.size(); i++) { -// if (commands.get(i).getName().equals(USE_COMMAND)) { -// commands.set(i, new Command(USE_COMMAND, 1) { -// @Override -// protected boolean action(InterpreterState state, String[] arguments) { -// StoreableTable currentTable; -// int changesCount = 0; -// currentTable = getCurrentTableWithoutMessages(state); -// if (currentTable != null) { -// changesCount = currentTable.changesCount(); -// } -// if (changesCount == 0) { -// String name = arguments[0]; -// try { -// MultiFileHashMap.toDatabaseState(state).getDatabase().useTable(name); -// System.out.println("using " + name); -// } catch (TableNotFoundException ex) { -// System.out.println(name + " not exists"); -// } catch (DatabaseFileStructureException | LoadOrSaveException e) { -// System.err.println(e.getMessage()); -// return false; -// } -// } else { -// System.out.println(changesCount + " unsaved changes"); -// } -// return true; -// } -// }); -// } -// if (commands.get(i).getName().equals(EXIT_COMMAND)) { -// commands.set(i, new Command(EXIT_COMMAND, 0) { -// @Override -// protected boolean action(InterpreterState state, String[] arguments) { -// StoreableTable currentTable = getCurrentTableWithoutMessages(state); -// int changesCount = 0; -// if (currentTable != null) { -// changesCount = currentTable.changesCount(); -// } -// if (changesCount == 0) { -// state.exit(); -// return false; -// } else { -// System.out.println(changesCount + " unsaved changes"); -// } -// return true; -// } -// }); -// } -// } -// commands.add(new Command("commit", 0) { -// @Override -// protected boolean action(InterpreterState state, String[] arguments) { -// try { -// System.out.println(getCurrentTable(state).commit()); -// return true; -// } catch (DatabaseFileStructureException | LoadOrSaveException e) { -// System.err.println(e.getMessage()); -// return false; -// } catch (TableNotFoundException e) { -// return true; -// } -// } -// }); -// commands.add(new Command("rollback", 0) { -// @Override -// protected boolean action(InterpreterState state, String[] arguments) { -// try { -// System.out.println(getCurrentTable(state).rollback()); -// } catch (TableNotFoundException e) { -// return true; -// } -// return NO_TABLE_RETURN_CODE; -// } -// }); -// commands.add(new Command("size", 0) { -// @Override -// protected boolean action(InterpreterState state, String[] arguments) { -// try { -// System.out.println(getCurrentTable(state).count()); -// return true; -// } catch (LoadOrSaveException | DatabaseFileStructureException e) { -// System.err.println(e.getMessage()); -// return false; -// } catch (TableNotFoundException e) { -// return NO_TABLE_RETURN_CODE; -// } -// } -// }); -// runDatabaseInterpreter(args, state, commands); -// } catch (DatabaseFileStructureException | LoadOrSaveException ex) { -// System.err.println(ex.getMessage()); -// System.exit(4); -// } -// } -// -// private static StoreableTable getCurrentTableWithoutMessages(InterpreterState state) { -// return (StoreableTable) ((DatabaseState) state).getDatabase().getCurrentTable(); -// } -// -// private static StoreableTable getCurrentTable(InterpreterState state) throws TableNotFoundException { -// return ((StoreableTable) MultiFileHashMap.getCurrentTable(state)); -// } -// -// private static void runDatabaseInterpreter(String[] args, DatabaseState state, List commands) { -// Interpreter interpreter = new Interpreter(commands, state); -// interpreter.run(args); -// if (args.length != 0) { -// if (!state.tryToSave()) { -// System.exit(2); -// } -// } -// } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ArgumentException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ArgumentException.java new file mode 100644 index 000000000..e4206d659 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ArgumentException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter; + +public class ArgumentException extends ParserException { + public ArgumentException(String message) { + super(message); + } + + public ArgumentException(String message, Throwable ex) { + super(message, ex); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Command.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Command.java new file mode 100644 index 000000000..701cfc2fa --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Command.java @@ -0,0 +1,37 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter; + +public abstract class Command { + private String name; + private int argumentsCount; + + public Command(String commandName, int argCount) { + name = commandName; + argumentsCount = argCount; + } + + public Command(String commandName) { + name = commandName; + argumentsCount = -1; + } + + public boolean invoke(InterpreterState interpreterState, ParsedCommand command, StreamsContainer streams) + throws ArgumentException, ParserException { + if (!command.getCommandName().equals(name)) { + throw new IllegalArgumentException("Wrong interpreter command: " + command.getCommandName() + + ", but expected " + name); + } + if (argumentsCount >= 0 && command.getArguments().length != argumentsCount) { + throw new ArgumentException("Invalid number of arguments: " + argumentsCount + + " expected, " + command.getArguments().length + " found"); + } else { + return action(interpreterState, command.getArguments(), streams); + } + } + + protected abstract boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException; + + public String getName() { + return name; + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java new file mode 100644 index 000000000..dc6642989 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java @@ -0,0 +1,302 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter; + +import java.util.*; + +public class Interpreter { + private boolean hasCorrectTerminated = false; + + private InterpreterState currentState; + private StreamsContainer streams; + private List commandsList; + private int exitCode = 0; + + public Interpreter(List commands, InterpreterState interpreterState, StreamsContainer container) { + commandsList = commands; + currentState = interpreterState; + streams = container; + } + + private static ArrayList splitArguments(ArrayList currentCommand) + throws CannotParseCommandException { + ArrayList arguments = new ArrayList<>(); + for (int i = 0; i < currentCommand.size(); ++i) { + CommandToken token = currentCommand.get(i); + if (token.isWasInQuotes()) { + arguments.add(token.getValue()); + if (i + 1 < currentCommand.size() && (!currentCommand.get(i + 1).isWasInQuotes())) { + currentCommand.get(i + 1).setValue(trimOneStartSpace(currentCommand.get(i + 1).getValue())); + } + } else { + if (i + 1 < currentCommand.size() && currentCommand.get(i + 1).isWasInQuotes()) { + currentCommand.get(i).setValue(trimOneEndSpace(currentCommand.get(i).getValue())); + } + if (token.getValue().length() > 0) { + arguments.addAll(Arrays.asList(token.getValue().split("\\s"))); + } + } + } + ArrayList emptyArgs = new ArrayList<>(); + for (String arg : arguments) { + if (arg.equals("")) { + emptyArgs.add(arg); + } + } + arguments.removeAll(emptyArgs); + return arguments; + } + + private static String trimOneEndSpace(String line) throws CannotParseCommandException { + if (line.length() != 0) { + char endChar = line.charAt(line.length() - 1); + if (endChar == ' ' || endChar == '\t') { + return line.substring(0, line.length() - 1); + } else { + throw new CannotParseCommandException("Where is no space between to arguments."); + } + } else { + return line; + } + } + + private static String trimOneStartSpace(String line) throws CannotParseCommandException { + if (line.length() == 0) { + return line; + } else { + char startChar = line.charAt(0); + if (startChar == ' ' || startChar == '\t') { + return line.substring(1); + } else { + throw new CannotParseCommandException("Where is no space between to arguments."); + } + } + } + + private List parseCommand(String input) throws ParserException { + List tokensByQuote = splitCommandByQuote(input); + ArrayList> commandsTokens = splitCommands(tokensByQuote); + return getParsedCommands(commandsTokens); + } + + private List parseCommand(String[] input) throws ParserException { + ArrayList> commandsTokens = new ArrayList<>(); + commandsTokens.add(new ArrayList<>()); + for (String arg : input) { + String[] tokens = arg.split(";", -1); + addAtEnd(commandsTokens, tokens[0]); + for (int i = 1; i < tokens.length; ++i) { + commandsTokens.add(new ArrayList<>()); + addAtEnd(commandsTokens, tokens[i]); + } + } + return getParsedCommands(commandsTokens); + } + + private void addAtEnd(ArrayList> commandsTokens, String arg) { + commandsTokens.get(commandsTokens.size() - 1).add(new CommandToken(arg, false)); + } + + private ArrayList> splitCommands(List tokensByQuote) { + ArrayList> commandsTokens = new ArrayList<>(tokensByQuote.size()); + commandsTokens.add(new ArrayList<>()); + for (CommandToken token : tokensByQuote) { + if (token.isWasInQuotes()) { + commandsTokens.get(commandsTokens.size() - 1).add(token); + } else { + String[] tokens = token.getValue().split(";", -1); + for (int j = 0; j < tokens.length; ++j) { + if (j > 0) { + commandsTokens.add(new ArrayList<>()); + } + addAtEnd(commandsTokens, tokens[j]); + } + } + } + return commandsTokens; + } + + private List splitCommandByQuote(String input) throws CannotParseCommandException { + List result = new ArrayList<>(); + int startIndex = 0; + while (startIndex >= 0) { + int index = input.indexOf('"', startIndex); + if (index < 0) { + result.add(new CommandToken(input.substring(startIndex), false)); + } else { + result.add(new CommandToken(input.substring(startIndex, index), false)); + startIndex = index + 1; + index = input.indexOf('"', startIndex); + if (index < 0) { + throw new CannotParseCommandException("Wrong quote structure."); + } else { + result.add(new CommandToken(input.substring(startIndex, index), true)); + } + } + startIndex = index; + if (startIndex > 0) { + ++startIndex; + } + } + return result; + } + + private List getParsedCommands(ArrayList> commandsTokens) + throws CannotParseCommandException { + List commands = new ArrayList<>(commandsTokens.size()); + for (ArrayList currentCommand : commandsTokens) { + ArrayList arguments = splitArguments(currentCommand); + ParsedCommand result = new ParsedCommand(); + result.setCommandName(((arguments.size() > 0) ? arguments.get(0) : null)); + String[] realArguments = new String[(arguments.size() > 0) ? arguments.size() - 1 : 0]; + for (int i = 1; i < arguments.size(); ++i) { + realArguments[i - 1] = arguments.get(i); + } + result.setArguments(realArguments); + commands.add(result); + } + + return commands; + } + + public final void run(String[] args) { + if (args.length == 0) { + Scanner scanner = new Scanner(streams.getIn()); + String command; + do { + try { + streams.getOut().print("$ "); + command = scanner.nextLine(); + invokeCommand(command); + } catch (NoSuchElementException ex) { + streams.getErr().println("Error: Can not read"); + break; + } + } while (!isCorrectTerminated()); + } else { + if (invokeCommand(args)) { + hasCorrectTerminated = true; + } + } + if (!isCorrectTerminated()) { + exitCode = 1; + } + } + + public boolean invokeCommand(String input) { + try { + List commands = parseCommand(input); + return invokeCommands(commands); + } catch (CommandInvokeException ex) { + printInvokeError(ex); + } catch (ParserException ex) { + streams.getErr().println("Error: " + ex.getMessage()); + } + streams.getErr().flush(); + return false; + } + + protected void printInvokeError(CommandInvokeException ex) { + streams.getErr().println(new StringBuilder().append(ex.getCommandName()).append(": ").append(ex.getMessage())); + } + + public boolean invokeCommand(String[] input) { + try { + List commands = parseCommand(input); + return invokeCommands(commands); + } catch (CommandInvokeException ex) { + streams.getErr().println(new StringBuilder().append(ex.getCommandName()).append(": ") + ex.getMessage()); + } catch (ParserException ex) { + streams.getErr().println("Error: " + ex.getMessage()); + } + streams.getErr().flush(); + return false; + } + + protected boolean invokeCommands(List commands) throws ParserException { + for (ParsedCommand command : commands) { + if (command.getCommandName() != null) { + try { + boolean foundCommand = false; + for (Command cmd : commandsList) { + if (cmd.getName().equals(command.getCommandName())) { + foundCommand = true; + if (!cmd.invoke(currentState, command, streams)) { + if (currentState.exited()) { + return exit(); + } else { + return false; + } + } + break; + } + } + if (!foundCommand) { + throw new ParserException("This command is unknown: " + command.getCommandName()); + } + } catch (ArgumentException ex) { + streams.getErr().println(ex.getMessage()); + return false; + } + } + } + return true; + } + + protected final boolean exit() { + this.hasCorrectTerminated = true; + return false; + } + + public boolean isCorrectTerminated() { + return hasCorrectTerminated; + } + + public int getExitCode() { + return exitCode; + } + + private static class CommandToken { + private String value; + private boolean wasInQuotes; + + CommandToken(String value, boolean wasInQuotes) { + this.value = value; + this.wasInQuotes = wasInQuotes; + } + + String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + boolean isWasInQuotes() { + return wasInQuotes; + } + } +} + +class CannotParseCommandException extends ParserException { + CannotParseCommandException(String message) { + super(message); + } +} + +class CommandInvokeException extends ParserException { + private final String commandName; + + CommandInvokeException(String message, String commandName) { + super(message); + this.commandName = commandName; + } + + CommandInvokeException(String message, String commandName, Throwable ex) { + super(message, ex); + this.commandName = commandName; + } + + public String getCommandName() { + return commandName; + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/InterpreterState.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/InterpreterState.java new file mode 100644 index 000000000..82417b40d --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/InterpreterState.java @@ -0,0 +1,13 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter; + +public class InterpreterState { + private boolean run = true; + + public boolean exited() { + return !run; + } + + public void exit() { + run = false; + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParsedCommand.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParsedCommand.java new file mode 100644 index 000000000..1ae27619b --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParsedCommand.java @@ -0,0 +1,22 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter; + +public class ParsedCommand { + private String[] arguments; + private String commandName; + + public String getCommandName() { + return commandName; + } + + public void setCommandName(String commandName) { + this.commandName = commandName; + } + + public String[] getArguments() { + return arguments; + } + + public void setArguments(String[] arguments) { + this.arguments = arguments; + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParserException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParserException.java new file mode 100644 index 000000000..1a5f49602 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/ParserException.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter; + +public class ParserException extends Exception { + public ParserException(String message) { + super(message); + } + + public ParserException(String message, Throwable ex) { + super(message, ex); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java new file mode 100644 index 000000000..2ba4fd0da --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java @@ -0,0 +1,36 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter; + +import java.io.InputStream; +import java.io.PrintStream; + +public class StreamsContainer { + + private final PrintStream out; + private final PrintStream err; + private final InputStream in; + + public StreamsContainer(PrintStream out, PrintStream err, InputStream in) { + this.out = out; + this.err = err; + this.in = in; + } + + + public StreamsContainer(){ + out = System.out; + err = System.err; + in = System.in; + } + + public PrintStream getOut() { + return out; + } + + public PrintStream getErr() { + return err; + } + + public InputStream getIn() { + return in; + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java new file mode 100644 index 000000000..bd170740d --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java @@ -0,0 +1,214 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.Assert; +import org.junit.Test; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.*; + +public class CommandTest { + + public static final int TEST_COUNT = 10; + public static final String TEST_COMMAND_NAME = "testname"; + private InterpreterState state = new InterpreterState(); + private StreamsContainer streams=new StreamsContainer(); + + @Test + public void testInvoke() throws ParserException { + Command command = new Command(TEST_COMMAND_NAME, 2) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) { + return arguments[0].equals(arguments[1]); + } + }; + String[] args = new String[]{"1", "1"}; + ParsedCommand information = new ParsedCommand(); + information.setCommandName(TEST_COMMAND_NAME); + information.setArguments(args); + Assert.assertTrue(command.invoke(state, information, streams)); + args[1] = "2"; + Assert.assertFalse(command.invoke(state, information, streams)); + } + + @Test(expected = ArgumentException.class) + public void testWrongArgumentCountException() throws ParserException { + Command cmd = new Command(TEST_COMMAND_NAME, 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + throw new ParserException("Only for test"); + } + }; + ParsedCommand command = new ParsedCommand(); + command.setCommandName(TEST_COMMAND_NAME); + command.setArguments(new String[0]); + cmd.invoke(state, command, streams); + } + + @Test + public void testAutoCheckArguments() { + ParsedCommand[] arguments = new ParsedCommand[TEST_COUNT]; + int[] results = new int[TEST_COUNT]; + for (int i = 0; i < TEST_COUNT; i++) { + arguments[i] = new ParsedCommand(); + arguments[i].setCommandName(TEST_COMMAND_NAME); + String[] args = new String[i]; + int sum = 0; + for (int j = 0; j < i; j++) { + args[j] = j + ""; + sum += j; + } + results[i] = sum; + arguments[i].setArguments(args); + } + for (int i = 0; i < TEST_COUNT; i++) { + final int[] result = {0}; + Command cmd = new Command(TEST_COMMAND_NAME, i) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + int sum = 0; + for (String str : arguments) { + sum += Integer.parseInt(str); + } + result[0] = sum; + return true; + } + }; + for (int j = 0; j < TEST_COUNT; j++) { + result[0] = 0; + try { + Assert.assertTrue(cmd.invoke(state, arguments[j], streams)); + Assert.assertEquals(result[0], results[j]); + } catch (ParserException e) { + if (j == i) { + Assert.fail("Don't work with arguments"); + } + } + } + } + } + + @Test + public void testManualCheckArguments() throws ParserException { + testManualArgsCheckingWork(new Command(TEST_COMMAND_NAME) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + if (arguments.length == 2) { + return true; + } else { + throw new ArgumentException("My message"); + } + } + }); + testManualArgsCheckingWork(new Command(TEST_COMMAND_NAME, -1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + if (arguments.length == 2) { + return true; + } else { + throw new ArgumentException("My message"); + } + } + }); + } + + private void testManualArgsCheckingWork(Command cmd) throws ParserException { + Assert.assertEquals(TEST_COMMAND_NAME, cmd.getName()); + ParsedCommand parsedCommand = new ParsedCommand(); + parsedCommand.setCommandName(TEST_COMMAND_NAME); + parsedCommand.setArguments(new String[]{"", ""}); + Assert.assertTrue(cmd.invoke(state, parsedCommand, streams)); + try { + parsedCommand.setArguments(new String[0]); + cmd.invoke(state, parsedCommand, streams); + Assert.fail("Manual check arguments don't work"); + } catch (ArgumentException e) { + Assert.assertEquals("My message", e.getMessage()); + } + } + + @Test + public void testGetName() throws Exception { + Command cmd = new Command(TEST_COMMAND_NAME, 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + return false; + } + }; + Assert.assertEquals(TEST_COMMAND_NAME, cmd.getName()); + cmd = new Command("someText", 1) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + return false; + } + }; + Assert.assertEquals("someText", cmd.getName()); + } + + @Test(expected = ParserException.class) + public void testCanThrowParserException() throws ParserException { + Command cmd = new Command(TEST_COMMAND_NAME, 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + throw new ParserException("Only for test"); + } + }; + ParsedCommand command = new ParsedCommand(); + command.setCommandName(TEST_COMMAND_NAME); + command.setArguments(new String[0]); + cmd.invoke(state, command, streams); + } + + @Test(expected = IllegalArgumentException.class) + public void testCheckCommandName() throws ParserException { + Command cmd = new Command(TEST_COMMAND_NAME, 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + return true; + } + }; + ParsedCommand parsedCommand = new ParsedCommand(); + parsedCommand.setCommandName("WrongName"); + parsedCommand.setArguments(new String[0]); + cmd.invoke(state, parsedCommand, streams); + } + + @Test(expected = IllegalArgumentException.class) + public void testCheckCommandNameSecondCtor() throws ParserException { + Command cmd = new Command(TEST_COMMAND_NAME) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) throws ArgumentException, ParserException { + return true; + } + }; + ParsedCommand parsedCommand = new ParsedCommand(); + parsedCommand.setCommandName("WrongName"); + parsedCommand.setArguments(new String[0]); + cmd.invoke(state, parsedCommand, streams); + } + + @Test + public void testStateExit() throws ParserException { + InterpreterState myState = new InterpreterState(); + Assert.assertFalse(myState.exited()); + Command cmd = new Command(TEST_COMMAND_NAME, 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + state.exit(); + return false; + } + }; + ParsedCommand parsedCommand = new ParsedCommand(); + parsedCommand.setCommandName(TEST_COMMAND_NAME); + parsedCommand.setArguments(new String[0]); + Assert.assertFalse(cmd.invoke(myState, parsedCommand, streams)); + Assert.assertTrue(myState.exited()); + } + +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java new file mode 100644 index 000000000..c63f3f9d3 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java @@ -0,0 +1,126 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.*; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.LoadOrSaveException; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.Database; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.DatabaseFactory; + +import java.io.File; +import java.io.IOException; + +public class DatabaseTest { + + @ClassRule + public static TemporaryFolder folder = new TemporaryFolder(); + private static File dbPath; + private static TableProvider db; + + @BeforeClass + public static void setUpClass() throws IOException { + dbPath = folder.newFolder("db"); + db = new DatabaseFactory().create(dbPath.getAbsolutePath()); + } + + @Before + public void setUp() throws Exception { + + } + + @After + public void tearDown() throws Exception { + + } + + @Test + public void testThrowIOException() throws Exception { + try { + Database.throwIOException(new LoadOrSaveException("Text1", new IOException("Text2"))); + Assert.fail("Have to throw exception"); + } catch (IOException e) { + Assert.assertEquals("Text2", e.getMessage()); + } + try { + Database.throwIOException(new LoadOrSaveException("Text1", new IndexOutOfBoundsException("Text2"))); + Assert.fail("Have to throw exception"); + } catch (IOException e) { + Assert.assertEquals("Text1", e.getMessage()); + } + try { + Database.throwIOException(new LoadOrSaveException("Text1")); + Assert.fail("Have to throw exception"); + } catch (IOException e) { + Assert.assertEquals("Text1", e.getMessage()); + } + try { + Database.throwIOException(new DatabaseFileStructureException("Text1", new IOException("Text2"))); + Assert.fail("Have to throw exception"); + } catch (IOException e) { + Assert.assertEquals("Text2", e.getMessage()); + } + try { + Database.throwIOException(new DatabaseFileStructureException("Text1", + new IndexOutOfBoundsException("Text2"))); + Assert.fail("Have to throw exception"); + } catch (IOException e) { + Assert.assertEquals("Text1", e.getMessage()); + } + try { + Database.throwIOException(new DatabaseFileStructureException("Text1")); + Assert.fail("Have to throw exception"); + } catch (IOException e) { + Assert.assertEquals("Text1", e.getMessage()); + } + } + + @Test(expected = IOException.class) + public void testFileFoundInRootDirectory() throws IOException { + new Database(dbPath.getAbsolutePath()) { + void Test() throws IOException { + fileFoundInRootDirectory(new File("Test")); + } + }.Test(); + } + + @Test + public void testGetTable() throws Exception { + + } + + @Test + public void testCreateTable() throws Exception { + + } + + @Test + public void testGetRootDirectoryPath() throws Exception { + + } + + @Test + public void testRemoveTable() throws Exception { + + } + + @Test + public void testDeserialize() throws Exception { + + } + + @Test + public void testSerialize() throws Exception { + + } + + @Test + public void testCreateFor() throws Exception { + + } + + @Test + public void testCreateFor1() throws Exception { + + } +} \ No newline at end of file diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java new file mode 100644 index 000000000..1eb8e2b8a --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java @@ -0,0 +1,265 @@ +/*package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.*; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.Command; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.Interpreter; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.InterpreterState; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +public class InterpreterTest { + public static final String TEST_COMMAND_NAME = "test"; + private static PrintStream standartOut; + private static PrintStream standartErr; + private static InputStream standartIn; + private static ByteArrayOutputStream testOutput; + private static PrintStream testOut; + private static ByteArrayOutputStream testError; + private static PrintStream testErr; + + @BeforeClass + public static void setUp() { + standartOut = System.out; + standartErr = System.err; + standartIn = System.in; + testOutput = new ByteArrayOutputStream(); + testOut = new PrintStream(testOutput); + testError = new ByteArrayOutputStream(); + testErr = new PrintStream(testError); + System.setOut(testOut); + System.setErr(testErr); + } + + @AfterClass + public static void tearDown() { + System.setOut(standartOut); + System.setIn(standartIn); + System.setErr(standartErr); + } + + @Test + public void testInvokeCommands() { + Interpreter interpreter = createInterpreterWithTestCommand(); + interpreter.run(new String[]{TEST_COMMAND_NAME}); + Assert.assertEquals("TestMessage" + System.lineSeparator(), testOutput.toString()); + } + + @Test + public void testBatchModeWithOneCommand() { + Interpreter interpreter = createInterpreterWithTestCommand(); + interpreter.run(new String[]{TEST_COMMAND_NAME + ";", TEST_COMMAND_NAME + ";", TEST_COMMAND_NAME}); + String expected = "TestMessage" + System.lineSeparator() + "TestMessage" + + System.lineSeparator() + "TestMessage" + System.lineSeparator(); + Assert.assertEquals(expected, testOutput.toString()); + } + + @Test + public void testInteractiveModeWithOneCommand() { + List commands = new ArrayList<>(); + commands.add(new Command("exit", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.println("TestMessage"); + state.exit(); + return false; + } + }); + Interpreter interpreter = new Interpreter(commands, new InterpreterState()); + System.setIn(new ByteArrayInputStream(("exit" + System.lineSeparator()).getBytes())); + interpreter.run(new String[0]); + String expected = "$ TestMessage" + System.lineSeparator(); + Assert.assertEquals(expected, testOutput.toString()); + } + + @Test + public void testInteractiveModeWithSeveralCommands() { + Interpreter interpreter = createInterpreterWithTestAndExitCommands(); + System.setIn(new ByteArrayInputStream((TEST_COMMAND_NAME + System.lineSeparator() + TEST_COMMAND_NAME + + System.lineSeparator() + "exit" + System.lineSeparator()).getBytes())); + interpreter.run(new String[0]); + String expected = "$ TestMessage" + System.lineSeparator() + "$ TestMessage" + System.lineSeparator() + + "$ Terminated" + System.lineSeparator(); + Assert.assertEquals(expected, testOutput.toString()); + } + + @Test + public void testBatchModeWithSeveralCommands() { + Interpreter interpreter = createInterpreterWithTestAndExitCommands(); + interpreter.run(new String[]{TEST_COMMAND_NAME + ";", TEST_COMMAND_NAME}); + String expected = "TestMessage" + System.lineSeparator() + "TestMessage" + System.lineSeparator(); + Assert.assertEquals(expected, testOutput.toString()); + } + + @Test + public void testExitCommandInBatchMode() { + Interpreter interpreter = createInterpreterWithTestAndExitCommands(); + interpreter.run(new String[]{TEST_COMMAND_NAME + ";exit;", TEST_COMMAND_NAME}); + String expected = "TestMessage" + System.lineSeparator() + "Terminated" + System.lineSeparator(); + Assert.assertEquals(expected, testOutput.toString()); + } + + @Test + public void testCorrectArgumentsParsingBatchMode() { + List commands = new ArrayList<>(); + commands.add(new Command("print") { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.print(arguments.length); + for (String str : arguments) { + System.out.print(" " + str); + } + return true; + } + }); + + compareBatch(commands, new String[]{"print\t"}, "0"); + compareBatch(commands, new String[]{"print "}, "0"); + compareBatch(commands, new String[]{"\tprint"}, "0"); + compareBatch(commands, new String[]{" print"}, "0"); + compareBatch(commands, new String[]{"print\t\t"}, "0"); + compareBatch(commands, new String[]{"print "}, "0"); + compareBatch(commands, new String[]{"\t\tprint"}, "0"); + compareBatch(commands, new String[]{" print"}, "0"); + compareBatch(commands, new String[]{"print\t "}, "0"); + compareBatch(commands, new String[]{"print \t"}, "0"); + compareBatch(commands, new String[]{"\t print"}, "0"); + compareBatch(commands, new String[]{" \tprint"}, "0"); + + compareBatch(commands, new String[]{"print", "\t; print", "123"}, "01 123"); + compareBatch(commands, new String[]{"print", "\t; print", "123", "45\t;print ;print"}, "02 123 4500"); + compareBatch(commands, new String[]{"print", "\t; print", "123", "45;\tprint; print"}, "02 123 4500"); + compareBatch(commands, new String[]{"print", "\t; print", "123", "45\t;\tprint ; print"}, "02 123 4500"); + compareBatch(commands, new String[]{"print", "\t; print", "123", "45 ;\tprint ;\tprint"}, "02 123 4500"); + compareBatch(commands, new String[]{"print", "\t; print", "123", "45\t; print\t; print"}, "02 123 4500"); + + compareBatch(commands, new String[]{"\tprint"}, "0"); + compareBatch(commands, new String[]{"\t\tprint"}, "0"); + compareBatch(commands, new String[]{" \tprint"}, "0"); + compareBatch(commands, new String[]{"print "}, "0"); + compareBatch(commands, new String[]{"print\t"}, "0"); + compareBatch(commands, new String[]{"print \t"}, "0"); + compareBatch(commands, new String[]{"print\t "}, "0"); + compareBatch(commands, new String[]{"print "}, "0"); + compareBatch(commands, new String[]{"print\t\t"}, "0"); + compareBatch(commands, new String[]{"print "}, "0"); + compareBatch(commands, new String[]{"\tprint\t"}, "0"); + compareBatch(commands, new String[]{" print \t"}, "0"); + compareBatch(commands, new String[]{"\t\tprint\t "}, "0"); + compareBatch(commands, new String[]{"print "}, "0"); + compareBatch(commands, new String[]{"print\t\t"}, "0"); + + commands.add(new Command("secondCommand") { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.println("Output"); + return true; + } + }); + compareBatch(commands, new String[]{"print", ";secondCommand;print"}, + "0Output" + System.lineSeparator() + "0"); + } + + @Test + public void testCorrectArgumentsParsingInteractiveMode() { + List commands = new ArrayList<>(); + commands.add(new Command("print") { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.print(arguments.length); + for (String str : arguments) { + System.out.print(" " + str); + } + return true; + } + }); + commands.add(new Command("exit", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.println("Terminated"); + state.exit(); + return false; + } + }); + compareInteractive(commands, "print 345 abc \"de fg\" \"hij\"\t \tklmn;print\t;print\t;exit", + "$ 5 345 abc de fg hij klmn00Terminated" + System.lineSeparator()); + compareInteractive(commands, " print \t\t\t\t\t test \t\t\t " + + System.lineSeparator() + "exit", "$ 1 test$ Terminated" + System.lineSeparator() + ); + compareInteractive(commands, + "\t\t\t\t\t print\t\"qwe rty\tyu\" 1 2 3 \t\t test j;\tprint\t;\tprint; print ;exit", + "$ 6 qwe rty\tyu 1 2 3 test j000Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit\t", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit ", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "\texit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, " exit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit\t\t", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit ", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "\t\texit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, " exit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit\t ", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit \t", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "\t exit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, " \texit", "$ Terminated" + System.lineSeparator()); + } + + private void compareInteractive(List commands, String input, String expected) { + Interpreter interpreter; + prepareStreams(); + System.setIn(new ByteArrayInputStream(input.getBytes())); + interpreter = new Interpreter(commands, new InterpreterState()); + interpreter.run(new String[0]); + Assert.assertEquals(expected, testOutput.toString()); + } + + private void compareBatch(List commands, String[] input, String expected) { + Interpreter interpreter; + prepareStreams(); + interpreter = new Interpreter(commands, new InterpreterState()); + interpreter.run(input); + Assert.assertEquals(expected, testOutput.toString()); + } + + private Interpreter createInterpreterWithTestCommand() { + List commands = new ArrayList<>(); + commands.add(new Command(TEST_COMMAND_NAME, 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.println("TestMessage"); + return true; + } + }); + return new Interpreter(commands, new InterpreterState()); + } + + private Interpreter createInterpreterWithTestAndExitCommands() { + List commands = new ArrayList<>(); + commands.add(new Command(TEST_COMMAND_NAME, 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.println("TestMessage"); + return true; + } + }); + commands.add(new Command("exit", 0) { + @Override + protected boolean action(InterpreterState state, String[] arguments) { + System.out.println("Terminated"); + state.exit(); + return false; + } + }); + return new Interpreter(commands, new InterpreterState()); + } + + @Before + public void prepareStreams() { + testOutput.reset(); + testError.reset(); + } +} +*/ \ No newline at end of file From 7b8ccff5693ce9e19a81b59081ec406083840843 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Thu, 18 Dec 2014 17:35:51 +0300 Subject: [PATCH 23/36] Old interpreter tests --- .../storeable/tests/InterpreterTest.java | 119 ++++++++---------- 1 file changed, 53 insertions(+), 66 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java index 1eb8e2b8a..0009a758e 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java @@ -1,9 +1,9 @@ -/*package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; -import org.junit.*; -import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.Command; -import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.Interpreter; -import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.InterpreterState; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.interpreter.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -14,44 +14,25 @@ public class InterpreterTest { public static final String TEST_COMMAND_NAME = "test"; - private static PrintStream standartOut; - private static PrintStream standartErr; - private static InputStream standartIn; - private static ByteArrayOutputStream testOutput; - private static PrintStream testOut; - private static ByteArrayOutputStream testError; - private static PrintStream testErr; + private ByteArrayOutputStream testOutput = new ByteArrayOutputStream(); + private PrintStream testOut = new PrintStream(testOutput); + private ByteArrayOutputStream testError = new ByteArrayOutputStream(); + private PrintStream testErr = new PrintStream(testError); - @BeforeClass - public static void setUp() { - standartOut = System.out; - standartErr = System.err; - standartIn = System.in; - testOutput = new ByteArrayOutputStream(); - testOut = new PrintStream(testOutput); - testError = new ByteArrayOutputStream(); - testErr = new PrintStream(testError); - System.setOut(testOut); - System.setErr(testErr); - } - - @AfterClass - public static void tearDown() { - System.setOut(standartOut); - System.setIn(standartIn); - System.setErr(standartErr); + private StreamsContainer createStreamContainer(InputStream input) { + return new StreamsContainer(testOut, testErr, input); } @Test public void testInvokeCommands() { - Interpreter interpreter = createInterpreterWithTestCommand(); + Interpreter interpreter = createInterpreterWithTestCommand(null); interpreter.run(new String[]{TEST_COMMAND_NAME}); Assert.assertEquals("TestMessage" + System.lineSeparator(), testOutput.toString()); } @Test public void testBatchModeWithOneCommand() { - Interpreter interpreter = createInterpreterWithTestCommand(); + Interpreter interpreter = createInterpreterWithTestCommand(null); interpreter.run(new String[]{TEST_COMMAND_NAME + ";", TEST_COMMAND_NAME + ";", TEST_COMMAND_NAME}); String expected = "TestMessage" + System.lineSeparator() + "TestMessage" + System.lineSeparator() + "TestMessage" + System.lineSeparator(); @@ -63,14 +44,15 @@ public void testInteractiveModeWithOneCommand() { List commands = new ArrayList<>(); commands.add(new Command("exit", 0) { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.println("TestMessage"); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + streams.getOut().println("TestMessage"); state.exit(); return false; } }); - Interpreter interpreter = new Interpreter(commands, new InterpreterState()); - System.setIn(new ByteArrayInputStream(("exit" + System.lineSeparator()).getBytes())); + Interpreter interpreter = new Interpreter(commands, new InterpreterState(), + createStreamContainer(new ByteArrayInputStream(("exit" + System.lineSeparator()).getBytes()))); interpreter.run(new String[0]); String expected = "$ TestMessage" + System.lineSeparator(); Assert.assertEquals(expected, testOutput.toString()); @@ -78,9 +60,9 @@ protected boolean action(InterpreterState state, String[] arguments) { @Test public void testInteractiveModeWithSeveralCommands() { - Interpreter interpreter = createInterpreterWithTestAndExitCommands(); - System.setIn(new ByteArrayInputStream((TEST_COMMAND_NAME + System.lineSeparator() + TEST_COMMAND_NAME - + System.lineSeparator() + "exit" + System.lineSeparator()).getBytes())); + Interpreter interpreter = createInterpreterWithTestAndExitCommands( + new ByteArrayInputStream((TEST_COMMAND_NAME + System.lineSeparator() + TEST_COMMAND_NAME + + System.lineSeparator() + "exit" + System.lineSeparator()).getBytes())); interpreter.run(new String[0]); String expected = "$ TestMessage" + System.lineSeparator() + "$ TestMessage" + System.lineSeparator() + "$ Terminated" + System.lineSeparator(); @@ -89,7 +71,7 @@ public void testInteractiveModeWithSeveralCommands() { @Test public void testBatchModeWithSeveralCommands() { - Interpreter interpreter = createInterpreterWithTestAndExitCommands(); + Interpreter interpreter = createInterpreterWithTestAndExitCommands(null); interpreter.run(new String[]{TEST_COMMAND_NAME + ";", TEST_COMMAND_NAME}); String expected = "TestMessage" + System.lineSeparator() + "TestMessage" + System.lineSeparator(); Assert.assertEquals(expected, testOutput.toString()); @@ -97,7 +79,7 @@ public void testBatchModeWithSeveralCommands() { @Test public void testExitCommandInBatchMode() { - Interpreter interpreter = createInterpreterWithTestAndExitCommands(); + Interpreter interpreter = createInterpreterWithTestAndExitCommands(null); interpreter.run(new String[]{TEST_COMMAND_NAME + ";exit;", TEST_COMMAND_NAME}); String expected = "TestMessage" + System.lineSeparator() + "Terminated" + System.lineSeparator(); Assert.assertEquals(expected, testOutput.toString()); @@ -108,10 +90,11 @@ public void testCorrectArgumentsParsingBatchMode() { List commands = new ArrayList<>(); commands.add(new Command("print") { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.print(arguments.length); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + streams.getOut().print(arguments.length); for (String str : arguments) { - System.out.print(" " + str); + streams.getOut().print(" " + str); } return true; } @@ -155,8 +138,9 @@ protected boolean action(InterpreterState state, String[] arguments) { commands.add(new Command("secondCommand") { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.println("Output"); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + streams.getOut().println("Output"); return true; } }); @@ -169,18 +153,20 @@ public void testCorrectArgumentsParsingInteractiveMode() { List commands = new ArrayList<>(); commands.add(new Command("print") { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.print(arguments.length); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + streams.getOut().print(arguments.length); for (String str : arguments) { - System.out.print(" " + str); + streams.getOut().print(" " + str); } return true; } }); commands.add(new Command("exit", 0) { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.println("Terminated"); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + streams.getOut().println("Terminated"); state.exit(); return false; } @@ -210,8 +196,8 @@ protected boolean action(InterpreterState state, String[] arguments) { private void compareInteractive(List commands, String input, String expected) { Interpreter interpreter; prepareStreams(); - System.setIn(new ByteArrayInputStream(input.getBytes())); - interpreter = new Interpreter(commands, new InterpreterState()); + interpreter = new Interpreter(commands, new InterpreterState(), createStreamContainer( + new ByteArrayInputStream(input.getBytes()))); interpreter.run(new String[0]); Assert.assertEquals(expected, testOutput.toString()); } @@ -219,41 +205,43 @@ private void compareInteractive(List commands, String input, String exp private void compareBatch(List commands, String[] input, String expected) { Interpreter interpreter; prepareStreams(); - interpreter = new Interpreter(commands, new InterpreterState()); + interpreter = new Interpreter(commands, new InterpreterState(), createStreamContainer(null)); interpreter.run(input); Assert.assertEquals(expected, testOutput.toString()); } - private Interpreter createInterpreterWithTestCommand() { + private Interpreter createInterpreterWithTestCommand(InputStream input) { List commands = new ArrayList<>(); commands.add(new Command(TEST_COMMAND_NAME, 0) { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.println("TestMessage"); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) throws ArgumentException, ParserException { + streams.getOut().println("TestMessage"); return true; } }); - return new Interpreter(commands, new InterpreterState()); + return new Interpreter(commands, new InterpreterState(), createStreamContainer(input)); } - private Interpreter createInterpreterWithTestAndExitCommands() { + private Interpreter createInterpreterWithTestAndExitCommands(InputStream input) { List commands = new ArrayList<>(); commands.add(new Command(TEST_COMMAND_NAME, 0) { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.println("TestMessage"); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + streams.getOut().println("TestMessage"); return true; } }); commands.add(new Command("exit", 0) { @Override - protected boolean action(InterpreterState state, String[] arguments) { - System.out.println("Terminated"); + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + streams.getOut().println("Terminated"); state.exit(); return false; } }); - return new Interpreter(commands, new InterpreterState()); + return new Interpreter(commands, new InterpreterState(), createStreamContainer(input)); } @Before @@ -261,5 +249,4 @@ public void prepareStreams() { testOutput.reset(); testError.reset(); } -} -*/ \ No newline at end of file +} \ No newline at end of file From cced64d3ea1efb89e275afb4dff50140884b6a25 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sun, 21 Dec 2014 14:53:29 +0300 Subject: [PATCH 24/36] Interpreter fixes and tests, checkstyle --- .../LebedevAleksey/storeable/Database.java | 10 +- .../LebedevAleksey/storeable/Main.java | 2 +- .../storeable/interpreter/Interpreter.java | 49 +++++++- .../interpreter/StreamsContainer.java | 2 +- .../storeable/json/BrokenJsonException.java | 2 +- .../storeable/json/JsonParser.java | 2 +- .../storeable/tests/CommandTest.java | 5 +- .../storeable/tests/DatabaseTest.java | 6 +- .../storeable/tests/InterpreterTest.java | 112 ++++++++++++++---- .../storeable/tests/StringTableTest.java | 2 +- 10 files changed, 151 insertions(+), 41 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index 42144ec04..b87ac1d5a 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -94,7 +94,7 @@ public static void throwIOException(Throwable e) throws IOException { throw new IOException(e.getMessage(), e); } - public static List> ParseTypes(String[] input) throws ParseException { + public static List> parseTypes(String[] input) throws ParseException { List> types = new ArrayList<>(input.length); for (String item : input) { Class type = stringTypesMap.get(item); @@ -217,15 +217,15 @@ public Storeable deserialize(Table table, String value) throws ParseException { storable.setColumnAt(i, 0); } else if (data.get(i).getClass() == table.getColumnType(i)) { storable.setColumnAt(i, data.get(i)); - } else if (!tryCastInteger(table, i, data.get(i), storable) && - !tryCastFloat(table, i, data.get(i), storable)) { + } else if (!tryCastInteger(table, i, data.get(i), storable) + && !tryCastFloat(table, i, data.get(i), storable)) { throw new ParseException("Wrong data type in column number " + i, -1); } } return storable; } else { - throw new ParseException("Wrong size of list: have " + data.size() + ", table have " + - table.getColumnsCount() + " columns.", value.length() - 1); + throw new ParseException("Wrong size of list: have " + data.size() + ", table have " + + table.getColumnsCount() + " columns.", value.length() - 1); } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java index 4c387d535..dc13677a6 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java @@ -84,7 +84,7 @@ protected boolean action(InterpreterState state, String[] arguments, StreamsCont types = new String[0]; } if (toDatabaseState(state).getDatabase(). - createTable(name, Database.ParseTypes(types)) == null) { + createTable(name, Database.parseTypes(types)) == null) { streams.getOut().println(name + " exists"); return true; } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java index dc6642989..510aae89d 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/Interpreter.java @@ -3,6 +3,9 @@ import java.util.*; public class Interpreter { + public static final String SLASH_SPECIAL_STRING = "#s"; + public static final String QUOTE_SPECIAL_STRING = "#q"; + public static final String SEMICOLON_SPECIAL_STRING = "#d"; private boolean hasCorrectTerminated = false; private InterpreterState currentState; @@ -72,16 +75,25 @@ private static String trimOneStartSpace(String line) throws CannotParseCommandEx } private List parseCommand(String input) throws ParserException { + input = replaceSpecialChars(input); List tokensByQuote = splitCommandByQuote(input); ArrayList> commandsTokens = splitCommands(tokensByQuote); return getParsedCommands(commandsTokens); } + private String replaceSpecialChars(String input) { + input = input.replace("#", "##"); + input = input.replace("\\\\", SLASH_SPECIAL_STRING); + input = input.replace("\\\"", QUOTE_SPECIAL_STRING); + input = input.replace("\\;", SEMICOLON_SPECIAL_STRING); + return input; + } + private List parseCommand(String[] input) throws ParserException { ArrayList> commandsTokens = new ArrayList<>(); commandsTokens.add(new ArrayList<>()); for (String arg : input) { - String[] tokens = arg.split(";", -1); + String[] tokens = replaceSpecialChars(arg).split(";", -1); addAtEnd(commandsTokens, tokens[0]); for (int i = 1; i < tokens.length; ++i) { commandsTokens.add(new ArrayList<>()); @@ -145,10 +157,10 @@ private List getParsedCommands(ArrayList> for (ArrayList currentCommand : commandsTokens) { ArrayList arguments = splitArguments(currentCommand); ParsedCommand result = new ParsedCommand(); - result.setCommandName(((arguments.size() > 0) ? arguments.get(0) : null)); + result.setCommandName(((arguments.size() > 0) ? removeSpecialChars(arguments.get(0)) : null)); String[] realArguments = new String[(arguments.size() > 0) ? arguments.size() - 1 : 0]; for (int i = 1; i < arguments.size(); ++i) { - realArguments[i - 1] = arguments.get(i); + realArguments[i - 1] = removeSpecialChars(arguments.get(i)); } result.setArguments(realArguments); commands.add(result); @@ -157,6 +169,33 @@ private List getParsedCommands(ArrayList> return commands; } + private String removeSpecialChars(String text) { + text = replaceSpecialString(text, SLASH_SPECIAL_STRING, "\\"); + text = replaceSpecialString(text, SEMICOLON_SPECIAL_STRING, ";"); + text = replaceSpecialString(text, QUOTE_SPECIAL_STRING, "\""); + return text.replace("##", "#"); + } + + private String replaceSpecialString(String text, String mask, String maskValue) { + int index = 0; + do { + index = text.indexOf(mask, index); + if (index >= 0) { + int j = index - 1; + while (j >= 0 && text.charAt(j) == '#') { + j--; + } + if ((index - j) % 2 == 0) { + ++index; + } else { + text = text.substring(0, index) + maskValue + text.substring(index + mask.length()); + } + } + } + while (index >= 0); + return text; + } + public final void run(String[] args) { if (args.length == 0) { Scanner scanner = new Scanner(streams.getIn()); @@ -212,10 +251,12 @@ public boolean invokeCommand(String[] input) { } protected boolean invokeCommands(List commands) throws ParserException { + String lastCommand = ""; for (ParsedCommand command : commands) { if (command.getCommandName() != null) { try { boolean foundCommand = false; + lastCommand = command.getCommandName(); for (Command cmd : commandsList) { if (cmd.getName().equals(command.getCommandName())) { foundCommand = true; @@ -233,7 +274,7 @@ protected boolean invokeCommands(List commands) throws ParserExce throw new ParserException("This command is unknown: " + command.getCommandName()); } } catch (ArgumentException ex) { - streams.getErr().println(ex.getMessage()); + streams.getErr().println(lastCommand + ": " + ex.getMessage()); return false; } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java index 2ba4fd0da..e65ceef9f 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/interpreter/StreamsContainer.java @@ -16,7 +16,7 @@ public StreamsContainer(PrintStream out, PrintStream err, InputStream in) { } - public StreamsContainer(){ + public StreamsContainer() { out = System.out; err = System.err; in = System.in; diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java index b9f4a23f9..ba09a1b0f 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/BrokenJsonException.java @@ -1,7 +1,7 @@ package ru.fizteh.fivt.students.LebedevAleksey.storeable.json; public class BrokenJsonException extends Exception { - private int offsetError; + private final int offsetError; public BrokenJsonException(String message, int offsetError) { super(message); diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java index a8acf954a..3a1c5de87 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/json/JsonParser.java @@ -410,7 +410,7 @@ private static void createJson(Map data, StringBuilder builder) throw } class JsonTerminatedException extends Exception { - private int offset; + private final int offset; public JsonTerminatedException(int index) { super(); diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java index bd170740d..3df6d47f6 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/CommandTest.java @@ -9,7 +9,7 @@ public class CommandTest { public static final int TEST_COUNT = 10; public static final String TEST_COMMAND_NAME = "testname"; private InterpreterState state = new InterpreterState(); - private StreamsContainer streams=new StreamsContainer(); + private StreamsContainer streams = new StreamsContainer(); @Test public void testInvoke() throws ParserException { @@ -182,7 +182,8 @@ protected boolean action(InterpreterState state, String[] arguments, StreamsCont public void testCheckCommandNameSecondCtor() throws ParserException { Command cmd = new Command(TEST_COMMAND_NAME) { @Override - protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) throws ArgumentException, ParserException { + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { return true; } }; diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java index c63f3f9d3..e423fe60e 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java @@ -78,10 +78,10 @@ public void testThrowIOException() throws Exception { @Test(expected = IOException.class) public void testFileFoundInRootDirectory() throws IOException { new Database(dbPath.getAbsolutePath()) { - void Test() throws IOException { + void test() throws IOException { fileFoundInRootDirectory(new File("Test")); } - }.Test(); + }.test(); } @Test @@ -123,4 +123,4 @@ public void testCreateFor() throws Exception { public void testCreateFor1() throws Exception { } -} \ No newline at end of file +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java index 0009a758e..a6eec2d82 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java @@ -150,6 +150,36 @@ protected boolean action(InterpreterState state, String[] arguments, StreamsCont @Test public void testCorrectArgumentsParsingInteractiveMode() { + List commands = getPrintEndExitCommands(); + compareInteractive(commands, "print 345 abc \"de fg\" \"hij\"\t \tklmn;print\t;print\t;exit", + "$ 5 345 abc de fg hij klmn00Terminated" + System.lineSeparator()); + compareInteractive(commands, " print \t\t\t\t\t test \t\t\t " + + System.lineSeparator() + "exit", "$ 1 test$ Terminated" + System.lineSeparator() + ); + compareInteractive(commands, + "\t\t\t\t\t print\t\"qwe rty\tyu\" 1 2 3 \t\t test j;\tprint\t;\tprint; print ;exit", + "$ 6 qwe rty\tyu 1 2 3 test j000Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit\t", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit ", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "\texit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, " exit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit\t\t", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit ", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "\t\texit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, " exit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit\t ", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "exit \t", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "\t exit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, " \texit", "$ Terminated" + System.lineSeparator()); + compareInteractive(commands, "print \"[1,\\\"1234\\\"]\"; exit", + "$ 1 [1,\"1234\"]Terminated" + System.lineSeparator()); + compareInteractive(commands, "print \"[1,\\\"1234\\\"]\" \\;\\\\#; exit", + "$ 2 [1,\"1234\"] ;\\#Terminated" + System.lineSeparator()); + compareInteractive(commands, "print \"[1,#\\\"1234\\\"]\" ##\\;\\\\#; exit", + "$ 2 [1,#\"1234\"] ##;\\#Terminated" + System.lineSeparator()); + } + + private List getPrintEndExitCommands() { List commands = new ArrayList<>(); commands.add(new Command("print") { @Override @@ -171,35 +201,23 @@ protected boolean action(InterpreterState state, String[] arguments, StreamsCont return false; } }); - compareInteractive(commands, "print 345 abc \"de fg\" \"hij\"\t \tklmn;print\t;print\t;exit", - "$ 5 345 abc de fg hij klmn00Terminated" + System.lineSeparator()); - compareInteractive(commands, " print \t\t\t\t\t test \t\t\t " - + System.lineSeparator() + "exit", "$ 1 test$ Terminated" + System.lineSeparator() - ); - compareInteractive(commands, - "\t\t\t\t\t print\t\"qwe rty\tyu\" 1 2 3 \t\t test j;\tprint\t;\tprint; print ;exit", - "$ 6 qwe rty\tyu 1 2 3 test j000Terminated" + System.lineSeparator()); - compareInteractive(commands, "exit\t", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "exit ", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "\texit", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, " exit", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "exit\t\t", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "exit ", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "\t\texit", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, " exit", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "exit\t ", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "exit \t", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, "\t exit", "$ Terminated" + System.lineSeparator()); - compareInteractive(commands, " \texit", "$ Terminated" + System.lineSeparator()); + return commands; } private void compareInteractive(List commands, String input, String expected) { + compareInteractive(commands, input, expected, 0, ""); + } + + + private void compareInteractive(List commands, String input, String expected, int exitCode, String error) { Interpreter interpreter; prepareStreams(); interpreter = new Interpreter(commands, new InterpreterState(), createStreamContainer( new ByteArrayInputStream(input.getBytes()))); interpreter.run(new String[0]); Assert.assertEquals(expected, testOutput.toString()); + Assert.assertEquals(exitCode, interpreter.getExitCode()); + Assert.assertEquals(error, testError.toString()); } private void compareBatch(List commands, String[] input, String expected) { @@ -208,13 +226,16 @@ private void compareBatch(List commands, String[] input, String expecte interpreter = new Interpreter(commands, new InterpreterState(), createStreamContainer(null)); interpreter.run(input); Assert.assertEquals(expected, testOutput.toString()); + Assert.assertEquals(0, interpreter.getExitCode()); + Assert.assertEquals("", testError.toString()); } private Interpreter createInterpreterWithTestCommand(InputStream input) { List commands = new ArrayList<>(); commands.add(new Command(TEST_COMMAND_NAME, 0) { @Override - protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) throws ArgumentException, ParserException { + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { streams.getOut().println("TestMessage"); return true; } @@ -249,4 +270,51 @@ public void prepareStreams() { testOutput.reset(); testError.reset(); } -} \ No newline at end of file + + @Test + public void testShortInput() { + Interpreter interpreter; + prepareStreams(); + interpreter = new Interpreter(getPrintEndExitCommands(), new InterpreterState(), createStreamContainer( + new ByteArrayInputStream("print 1".getBytes()))); + interpreter.run(new String[0]); + Assert.assertEquals("$ 1 1$ ", testOutput.toString()); + Assert.assertEquals(1, interpreter.getExitCode()); + Assert.assertEquals("Error: Can not read" + System.lineSeparator(), testError.toString()); + } + + @Test + public void testWrongInput() { + List commands = getPrintEndExitCommands(); + compareInteractive(commands, "print \";exit", "$ $ ", 1, "Error: Wrong quote structure." + + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); + compareInteractive(commands, "pr\"nt; exit", "$ $ ", 1, "Error: Wrong quote structure." + + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); + compareInteractive(commands, "print \"1\"2; exit", "$ $ ", 1, "Error: Where is no space between to arguments." + + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); + } + + + @Test + public void testExceptionInCommand() { + List commands = getPrintEndExitCommands(); + commands.add(new Command("tst") { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + throw new ArgumentException("Message"); + } + }); + commands.add(new Command("tst2") { + @Override + protected boolean action(InterpreterState state, String[] arguments, StreamsContainer streams) + throws ArgumentException, ParserException { + throw new ParserException("Message2"); + } + }); + compareInteractive(commands, "tst; exit", "$ $ ", 1, "tst: Message" + + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); + compareInteractive(commands, "tst2; exit", "$ $ ", 1, "Error: Message2" + + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); + } +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java index 95967c5bb..6415164f8 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableTest.java @@ -472,4 +472,4 @@ public void testRollback() { Assert.assertEquals(3, table.size()); AdditionalAssert.assertStringArrayContainsSameItemsAsList(new String[]{"a", "c", "b"}, table.list()); } -} \ No newline at end of file +} From dc9fcc71ab6ee093e8ab3b37801920b2937a7c2c Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sun, 21 Dec 2014 16:22:52 +0300 Subject: [PATCH 25/36] Tests and fixes --- .../LebedevAleksey/storeable/Database.java | 6 +- .../LebedevAleksey/storeable/Storeable.java | 20 ++- .../storeable/tests/DatabaseTest.java | 128 ++++++++++++--- .../storeable/tests/StoreableTableTest.java | 84 ++++++++++ .../storeable/tests/StoreableTest.java | 154 ++++++++++++++++++ 5 files changed, 356 insertions(+), 36 deletions(-) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index b87ac1d5a..412bd4029 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -235,14 +235,14 @@ private boolean tryCastInteger(Table table, int column, Object value, Storeable if (table.getColumnType(column) == Integer.class) { long val = (long) value; if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) { - result.setColumnAt(column, (int) value); + result.setColumnAt(column, new Integer((int) val)); return true; } } else { if (table.getColumnType(column) == Byte.class) { long val = (long) value; if (val >= Byte.MIN_VALUE && val <= Byte.MAX_VALUE) { - result.setColumnAt(column, (byte) val); + result.setColumnAt(column, new Byte((byte) val)); return true; } } @@ -257,7 +257,7 @@ private boolean tryCastFloat(Table table, int column, Object value, Storeable re if (value.getClass() == Double.class) { if (table.getColumnType(column) == Float.class) { double val = (double) value; - if (val >= Float.MIN_VALUE && val <= Float.MAX_VALUE) { + if (Math.abs(val) >= Float.MIN_VALUE && val <= Float.MAX_VALUE) { result.setColumnAt(column, (float) val); return true; } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java index ee4e4192d..d3d879f29 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Storeable.java @@ -34,10 +34,14 @@ public Storeable(List data, Table table) throws ColumnFormatException { @Override public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { - if (table.getColumnType(columnIndex) == value.getClass()) { + if (value == null) { data.set(columnIndex, value); } else { - throw new ColumnFormatException(INCORRECT_TYPE_MESSAGE); + if (table.getColumnType(columnIndex) == value.getClass()) { + data.set(columnIndex, value); + } else { + throw new ColumnFormatException(INCORRECT_TYPE_MESSAGE); + } } } @@ -48,7 +52,7 @@ public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { @Override public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return (Integer) assertColumnType(columnIndex, int.class); + return (Integer) assertColumnType(columnIndex, Integer.class); } private Object assertColumnType(int columnIndex, Class type) { @@ -61,27 +65,27 @@ private Object assertColumnType(int columnIndex, Class type) { @Override public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return (Long) assertColumnType(columnIndex, long.class); + return (Long) assertColumnType(columnIndex, Long.class); } @Override public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return (byte) assertColumnType(columnIndex, byte.class); + return (byte) assertColumnType(columnIndex, Byte.class); } @Override public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return (float) assertColumnType(columnIndex, float.class); + return (float) assertColumnType(columnIndex, Float.class); } @Override public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return (double) assertColumnType(columnIndex, double.class); + return (double) assertColumnType(columnIndex, Double.class); } @Override public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { - return (boolean) assertColumnType(columnIndex, boolean.class); + return (boolean) assertColumnType(columnIndex, Boolean.class); } @Override diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java index e423fe60e..68f45b70c 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java @@ -2,31 +2,32 @@ import org.junit.*; import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; import ru.fizteh.fivt.storage.structured.TableProvider; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.DatabaseFileStructureException; import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.LoadOrSaveException; +import ru.fizteh.fivt.students.LebedevAleksey.MultiFileHashMap.Pair; import ru.fizteh.fivt.students.LebedevAleksey.storeable.Database; import ru.fizteh.fivt.students.LebedevAleksey.storeable.DatabaseFactory; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.List; public class DatabaseTest { - @ClassRule - public static TemporaryFolder folder = new TemporaryFolder(); - private static File dbPath; - private static TableProvider db; - - @BeforeClass - public static void setUpClass() throws IOException { - dbPath = folder.newFolder("db"); - db = new DatabaseFactory().create(dbPath.getAbsolutePath()); - } + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + private File dbPath; + private TableProvider database; @Before - public void setUp() throws Exception { - + public void setUp() throws IOException { + dbPath = folder.newFolder("db"); + database = new DatabaseFactory().create(dbPath.getAbsolutePath()); } @After @@ -85,42 +86,119 @@ void test() throws IOException { } @Test - public void testGetTable() throws Exception { - + public void testGetAndCreateNonExistentTable() throws Exception { + database.createTable("test", Arrays.asList(String.class)); + Assert.assertNotNull(database.getTable("test")); + Assert.assertEquals("test", database.getTable("test").getName()); } - @Test - public void testCreateTable() throws Exception { + @Test(expected = IllegalArgumentException.class) + public void testCreateTableNullName() throws Exception { + database.createTable(null, Arrays.asList(String.class)); + } + @Test(expected = IllegalArgumentException.class) + public void testCreateTableNullTypes() throws Exception { + database.createTable("abc", null); } - @Test - public void testGetRootDirectoryPath() throws Exception { + @Test(expected = IllegalArgumentException.class) + public void testCreateTableNullInList() throws Exception { + database.createTable("abc", Arrays.asList(Integer.class, null)); + } + @Test(expected = IllegalArgumentException.class) + public void testRemoveNull() throws Exception { + database.removeTable(null); } @Test - public void testRemoveTable() throws Exception { - + public void testCreateExistingTable() throws Exception { + database.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class)); + Assert.assertNull(database.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class))); } @Test - public void testDeserialize() throws Exception { + public void testRemoveExistingTable() throws Exception { + database.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class)); + database.removeTable("table"); + Assert.assertNull(database.getTable("table")); + Assert.assertNotNull(database.createTable("table", Arrays.asList(String.class, + Long.class, Double.class))); + } + @Test(expected = IllegalStateException.class) + public void testRemoveNotExistentTable() throws Exception { + database.removeTable("notExist"); } @Test - public void testSerialize() throws Exception { - + public void testSerializeDeserialize() throws Exception { + Table table = database.createTable("table", Arrays.asList( + String.class, Integer.class, Boolean.class, Long.class, Double.class, Float.class, Byte.class)); + List expected = Arrays.asList("qwerty", Integer.MIN_VALUE, true, + 23L, 1.23, -3.4E4f, (byte) 127); + Storeable original = database.createFor(table, expected); + String serialized = database.serialize(table, original); + Storeable deserialized = database.deserialize(table, serialized); + for (int i = 0; i < expected.size(); i++) { + Assert.assertEquals(expected.get(i), deserialized.getColumnAt(i)); + } } @Test public void testCreateFor() throws Exception { - + Table table = database.createTable("table", Arrays.asList( + String.class, Integer.class, Boolean.class, Long.class, Double.class, Float.class, Byte.class)); + List values = Arrays.asList("qwerty", Integer.MAX_VALUE, true, + -23L, -1.23, 3.4E4f, (byte) 127); + Storeable storeable = database.createFor(table, values); + for (int i = 0; i < values.size(); i++) { + Assert.assertEquals(values.get(i), storeable.getColumnAt(i)); + } } @Test - public void testCreateFor1() throws Exception { + public void testCreateForEmptyAndStoreableSet() throws Exception { + Table table = database.createTable("table", Arrays.asList( + Integer.class, Long.class, String.class, Boolean.class, Double.class, Float.class, Byte.class)); + Storeable actual = database.createFor(table); + try { + actual.setColumnAt(0, 1); + actual.setColumnAt(0, -1); + actual.setColumnAt(1, -3L); + actual.setColumnAt(2, "qwerty"); + actual.setColumnAt(3, false); + actual.setColumnAt(3, true); + actual.setColumnAt(4, 5.5); + actual.setColumnAt(5, 2.3f); + actual.setColumnAt(6, ((byte) 1)); + for (int i = 0; i < 7; i++) { + actual.setColumnAt(i, null); + } + for (int i = 2; i < 7; i++) { + try { + actual.setColumnAt(i, 1); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // It's ok + } + } + } catch (ColumnFormatException e) { + Assert.fail("Error in setting column " + e); + } + } + @Test + public void testGetTableNames() throws Exception { + database.createTable("t1", Arrays.asList(String.class, Integer.class, Boolean.class)); + database.createTable("t2", Arrays.asList(Double.class, String.class, Long.class)); + List> real = ((Database) database).listTables(); + Assert.assertEquals(2, real.size()); + Assert.assertEquals(0, (int) real.get(0).getValue()); + Assert.assertEquals(0, (int) real.get(1).getValue()); + String s1 = real.get(0).getKey(); + String s2 = real.get(1).getKey(); + Assert.assertTrue((s1.equals("t1") && s2.equals("t2")) || (s1.equals("t2") && s2.equals("t1"))); } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java new file mode 100644 index 000000000..747685c4e --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java @@ -0,0 +1,84 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.storage.structured.TableProviderFactory; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.DatabaseFactory; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class StoreableTableTest { + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + private Table table; + private TableProvider provider; + private TableProviderFactory factory; + private Class[] types = {Integer.class, String.class, Boolean.class}; + private File tempFolder; + + public StoreableTableTest() { + this.factory = new DatabaseFactory(); + } + + @Before + public void setUp() throws Exception { + tempFolder = folder.newFolder(); + provider = factory.create(tempFolder.getAbsolutePath()); + table = provider.createTable("table", Arrays.asList(types)); + } + + @Test + public void testPutGet() throws Exception { + List expected = Arrays.asList(0, "qwerty", false); + table.put("line", provider.createFor(table, expected)); + Storeable actual = table.get("line"); + for (int i = 0; i < expected.size(); i++) { + assertEquals(expected.get(i), actual.getColumnAt(i)); + } + } + + @Test + public void testRemove() throws Exception { + List expected = Arrays.asList(1, "test", true); + table.put("qwerty", provider.createFor(table, expected)); + Storeable actual = table.remove("qwerty"); + for (int i = 0; i < expected.size(); i++) { + assertEquals(expected.get(i), actual.getColumnAt(i)); + } + } + + @Test + public void testSize() throws Exception { + List expected = Arrays.asList(3, "q", false); + table.put("a", provider.createFor(table, expected)); + table.put("b", provider.createFor(table, expected)); + assertEquals(2, table.size()); + } + + @Test + public void testGetColumnsCount() throws Exception { + assertEquals(3, table.getColumnsCount()); + } + + @Test + public void testGetColumnType() throws Exception { + for (int i = 0; i < types.length; i++) { + assertEquals(types[i], table.getColumnType(i)); + } + } + + @Test + public void testGetName() throws Exception { + assertEquals("table", table.getName()); + } +} \ No newline at end of file diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java new file mode 100644 index 000000000..f381cc240 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java @@ -0,0 +1,154 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.DatabaseFactory; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +public class StoreableTest { + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + private File dbPath; + private TableProvider database; + private Table table; + private Storeable storeable; + + @Before + public void setUp() throws IOException { + dbPath = folder.newFolder("db"); + database = new DatabaseFactory().create(dbPath.getAbsolutePath()); + table = database.createTable("table", Arrays.asList( + Integer.class, Long.class, String.class, Boolean.class, Double.class, Float.class, Byte.class)); + storeable = database.createFor(table); + storeable.setColumnAt(0, 1); + storeable.setColumnAt(1, -3L); + storeable.setColumnAt(2, "qwerty"); + storeable.setColumnAt(3, true); + storeable.setColumnAt(4, 5.5); + storeable.setColumnAt(5, 2.3f); + storeable.setColumnAt(6, ((byte) 1)); + } + + @Test + public void testCreateEmptyStoreable() { + storeable = database.createFor(table); + for (int i = 0; i < table.getColumnsCount(); i++) { + Assert.assertNull(storeable.getColumnAt(i)); + } + } + + @Test + public void testGetIntAt() { + Assert.assertEquals((Object) 1, storeable.getIntAt(0)); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (i != 0) { + try { + storeable.getIntAt(i); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // Ok. + } + } + } + } + + @Test + public void testGetLongAt() throws Exception { + Assert.assertEquals((Object) (-3L), storeable.getLongAt(1)); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (i != 1) { + try { + storeable.getLongAt(i); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // Ok. + } + } + } + } + + @Test + public void testGetByteAt() throws Exception { + Assert.assertEquals((Object) ((byte) 1), storeable.getByteAt(6)); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (i != 6) { + try { + storeable.getByteAt(i); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // Ok. + } + } + } + } + + @Test + public void testGetFloatAt() throws Exception { + Assert.assertEquals((Object) 2.3f, storeable.getFloatAt(5)); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (i != 5) { + try { + storeable.getFloatAt(i); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // Ok. + } + } + } + } + + @Test + public void testGetDoubleAt() throws Exception { + Assert.assertEquals((Object) 5.5, storeable.getDoubleAt(4)); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (i != 4) { + try { + storeable.getDoubleAt(i); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // Ok. + } + } + } + } + + @Test + public void testGetBooleanAt() throws Exception { + Assert.assertEquals((Object) true, storeable.getBooleanAt(3)); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (i != 3) { + try { + storeable.getBooleanAt(i); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // Ok. + } + } + } + } + + @Test + public void testGetStringAt() throws Exception { + Assert.assertEquals("qwerty", storeable.getStringAt(2)); + for (int i = 0; i < table.getColumnsCount(); i++) { + if (i != 2) { + try { + storeable.getStringAt(i); + Assert.fail("Should be exception"); + } catch (ColumnFormatException e) { + // Ok. + } + } + } + } +} \ No newline at end of file From 0a37cb6d756cf001baabdd0f0ed1d403bd0760b7 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sun, 21 Dec 2014 17:03:08 +0300 Subject: [PATCH 26/36] A few tests added and null-bug fix --- .../LebedevAleksey/storeable/Database.java | 2 +- .../storeable/tests/DatabaseTest.java | 24 ++++++++++++++++--- .../storeable/tests/InterpreterTest.java | 2 ++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index 412bd4029..868cc7a87 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -214,7 +214,7 @@ public Storeable deserialize(Table table, String value) throws ParseException { Storeable storable = createFor(table); for (int i = 0; i < data.size(); i++) { if (data.get(i) == null) { - storable.setColumnAt(i, 0); + storable.setColumnAt(i, null); } else if (data.get(i).getClass() == table.getColumnType(i)) { storable.setColumnAt(i, data.get(i)); } else if (!tryCastInteger(table, i, data.get(i), storable) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java index 68f45b70c..5545e18f5 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java @@ -14,6 +14,7 @@ import java.io.File; import java.io.IOException; +import java.text.ParseException; import java.util.Arrays; import java.util.List; @@ -134,18 +135,35 @@ public void testRemoveNotExistentTable() throws Exception { @Test public void testSerializeDeserialize() throws Exception { - Table table = database.createTable("table", Arrays.asList( - String.class, Integer.class, Boolean.class, Long.class, Double.class, Float.class, Byte.class)); + Table table = database.createTable("table", Arrays.asList(String.class, Integer.class, Boolean.class, + Long.class, Double.class, Float.class, Byte.class, Byte.class)); List expected = Arrays.asList("qwerty", Integer.MIN_VALUE, true, - 23L, 1.23, -3.4E4f, (byte) 127); + 23L, 1.23, -3.4E4f, (byte) 127, null); Storeable original = database.createFor(table, expected); String serialized = database.serialize(table, original); Storeable deserialized = database.deserialize(table, serialized); for (int i = 0; i < expected.size(); i++) { Assert.assertEquals(expected.get(i), deserialized.getColumnAt(i)); } + table = database.createTable("name", Arrays.asList(Integer.class, String.class)); + deserialized = database.deserialize(table, "[null,null]"); + Assert.assertNull(deserialized.getColumnAt(0)); + Assert.assertNull(deserialized.getColumnAt(1)); + } + + @Test(expected = ParseException.class) + public void testDeserializeWrongNumberOfColumns() throws ParseException, IOException { + Table table = database.createTable("table", Arrays.asList(String.class, Integer.class)); + database.deserialize(table, "[null]"); } + @Test(expected = ParseException.class) + public void testDeserializeWrongNumberOfColumns2() throws ParseException, IOException { + Table table = database.createTable("table", Arrays.asList(String.class, Integer.class)); + database.deserialize(table, "[null,0,0]"); + } + + @Test public void testCreateFor() throws Exception { Table table = database.createTable("table", Arrays.asList( diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java index a6eec2d82..a2a6f6109 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/InterpreterTest.java @@ -316,5 +316,7 @@ protected boolean action(InterpreterState state, String[] arguments, StreamsCont + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); compareInteractive(commands, "tst2; exit", "$ $ ", 1, "Error: Message2" + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); + compareInteractive(commands, "qwerty", "$ $ ", 1, "Error: This command is unknown: qwerty" + + System.lineSeparator() + "Error: Can not read" + System.lineSeparator()); } } From 81827a1e2220cdaebacf31e2c15e09035cc2743e Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sun, 21 Dec 2014 17:09:49 +0300 Subject: [PATCH 27/36] Fix bug in table with 0 columns --- .../LebedevAleksey/storeable/Database.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index 868cc7a87..4dc13ef33 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -63,12 +63,14 @@ public Database(String directory) throws IOException { } String[] tokens = signatureString.split(" "); List> types = new ArrayList<>(); - for (String item : tokens) { - Class type = stringTypesMap.get(item); - if (type == null) { - throw new IOException("Wrong type name in signature of table " + tablename); - } else { - types.add(type); + if (signatureString.length() != 0) { + for (String item : tokens) { + Class type = stringTypesMap.get(item); + if (type == null) { + throw new IOException("Wrong type name in signature of table " + tablename); + } else { + types.add(type); + } } } StoreableTable table = generateTable(tablename, types); From 30620d636314626094130483ad4627ff2abaed88 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sun, 21 Dec 2014 17:28:42 +0300 Subject: [PATCH 28/36] Storeable v1.0 (checkstyle) --- .../LebedevAleksey/storeable/tests/StoreableTableTest.java | 3 +-- .../students/LebedevAleksey/storeable/tests/StoreableTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java index 747685c4e..efcc1d442 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTableTest.java @@ -15,7 +15,6 @@ import java.util.List; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; public class StoreableTableTest { @Rule @@ -81,4 +80,4 @@ public void testGetColumnType() throws Exception { public void testGetName() throws Exception { assertEquals("table", table.getName()); } -} \ No newline at end of file +} diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java index f381cc240..52803f2de 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StoreableTest.java @@ -151,4 +151,4 @@ public void testGetStringAt() throws Exception { } } } -} \ No newline at end of file +} From 349b4edbc51073ca6508e5d60ba0f590611b5ded Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Sun, 21 Dec 2014 17:55:42 +0300 Subject: [PATCH 29/36] Storeable v2.0 (fix problems with changed interfaces) --- .../LebedevAleksey/storeable/Database.java | 9 +++++++++ .../students/LebedevAleksey/storeable/Main.java | 2 +- .../LebedevAleksey/storeable/StoreableTable.java | 11 ++++++++--- .../storeable/tests/DatabaseTest.java | 14 +++++++++++++- .../storeable/tests/StringTableWrapper.java | 6 +----- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index 4dc13ef33..80c743d34 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -298,6 +298,15 @@ public Storeable createFor(Table table, List values) throws ColumnFormatExcep return new ru.fizteh.fivt.students.LebedevAleksey.storeable.Storeable((List) values, table); } + @Override + public List getTableNames() { + final List result = new ArrayList<>(tables.size()); + tables.keySet().forEach((String s) -> { + result.add(s); + }); + return result; + } + private void assertArgumentNotNull(Object argument, String name) { if (argument == null) { throw new IllegalArgumentException("Argument \"" + name + "\" is null"); diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java index dc13677a6..178ab8b96 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Main.java @@ -186,7 +186,7 @@ protected boolean action(InterpreterState state, String[] arguments, StreamsCont } streams.getOut().println(); return true; - } catch (IOException | DatabaseException e) { + } catch (DatabaseException e) { streams.getErr().println(e.getMessage()); return false; } catch (TableNotFoundException e) { diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java index b1b288e23..e2ac911b2 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java @@ -147,6 +147,11 @@ public int rollback() { return changes; } + @Override + public int getNumberOfUncommittedChanges() { + return changesCount(); + } + @Override public String getName() { return name; @@ -186,7 +191,8 @@ public void drop() throws DatabaseFileStructureException, LoadOrSaveException { stringTable.drop(); } - public List list() throws IOException { + @Override + public List list() { try { Set items = new TreeSet<>(stringTable.list()); for (String key : changedKeys.keySet()) { @@ -206,8 +212,7 @@ public void accept(String s) { }); return result; } catch (DatabaseFileStructureException | LoadOrSaveException e) { - Database.throwIOException(e); - return null; // unreached + throw new DatabaseException(e); } } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java index 5545e18f5..9b7a50403 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/DatabaseTest.java @@ -208,7 +208,7 @@ public void testCreateForEmptyAndStoreableSet() throws Exception { } @Test - public void testGetTableNames() throws Exception { + public void testListTables() throws Exception { database.createTable("t1", Arrays.asList(String.class, Integer.class, Boolean.class)); database.createTable("t2", Arrays.asList(Double.class, String.class, Long.class)); List> real = ((Database) database).listTables(); @@ -219,4 +219,16 @@ public void testGetTableNames() throws Exception { String s2 = real.get(1).getKey(); Assert.assertTrue((s1.equals("t1") && s2.equals("t2")) || (s1.equals("t2") && s2.equals("t1"))); } + + + @Test + public void testGetTableNames() throws Exception { + database.createTable("t1", Arrays.asList(String.class, Integer.class, Boolean.class)); + database.createTable("t2", Arrays.asList(Double.class, String.class, Long.class)); + List real = ((Database) database).getTableNames(); + Assert.assertEquals(2, real.size()); + String s1 = real.get(0); + String s2 = real.get(1); + Assert.assertTrue((s1.equals("t1") && s2.equals("t2")) || (s1.equals("t2") && s2.equals("t1"))); + } } diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java index 8992fc436..c122189e8 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/StringTableWrapper.java @@ -82,10 +82,6 @@ public int rollback() { @Override public List list() { - try { - return table.list(); - } catch (IOException e) { - throw new DatabaseException(e); - } + return table.list(); } } From 1ec28a283dc8d4e8daff2b78faa2b98f731319cd Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Wed, 24 Dec 2014 11:18:14 +0300 Subject: [PATCH 30/36] Parallel table --- .../storeable/StoreableTable.java | 113 +++++++++++++----- 1 file changed, 83 insertions(+), 30 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java index e2ac911b2..d02db8beb 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java @@ -11,14 +11,23 @@ import java.nio.file.Path; import java.text.ParseException; import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; public class StoreableTable implements ru.fizteh.fivt.storage.structured.Table { private final String name; private final Database database; - private Map changedKeys = new TreeMap<>(); + private final ThreadLocal> changedKeys = new ThreadLocal>() { + @Override + protected Map initialValue() { + return new TreeMap<>(); + } + }; private Table stringTable; private List> columnTypes; + private ReadWriteLock lock = new ReentrantReadWriteLock(true); public StoreableTable(String name, Database databaseParent, List> types) { this.name = name; @@ -43,16 +52,16 @@ private void checkKeyValueNotNull(String key, Storeable value) { private String putStrings(String key, String value) throws DatabaseFileStructureException, LoadOrSaveException { String oldValue = getString(key); if (value.equals(stringTable.get(key))) { - changedKeys.remove(key); + changedKeys.get().remove(key); } else { - changedKeys.put(key, value); + changedKeys.get().put(key, value); } return oldValue; } private String getString(String key) throws LoadOrSaveException, DatabaseFileStructureException { - if (changedKeys.containsKey(key)) { - return changedKeys.get(key); + if (changedKeys.get().containsKey(key)) { + return changedKeys.get().get(key); } else { return stringTable.get(key); } @@ -61,6 +70,8 @@ private String getString(String key) throws LoadOrSaveException, DatabaseFileStr @Override public Storeable put(String key, Storeable value) throws ColumnFormatException { checkKeyValueNotNull(key, value); + Lock readLock = lock.readLock(); + readLock.lock(); try { String result = putStrings(key, database.serialize(this, value)); if (result == null) { @@ -72,6 +83,8 @@ public Storeable put(String key, Storeable value) throws ColumnFormatException { throw new DatabaseException(e); } catch (ParseException e) { throw new ColumnFormatException(e); + } finally { + readLock.unlock(); } } @@ -79,29 +92,36 @@ public Storeable put(String key, Storeable value) throws ColumnFormatException { public Storeable remove(String key) { checkKeyNotNull(key); String value; + Lock readLock = lock.readLock(); + readLock.lock(); try { value = stringTable.get(key); + Storeable oldValue = get(key); + if (value != null) { + if (oldValue != null) { + changedKeys.get().put(key, null); + } + } else { + changedKeys.get().remove(key); + } + return oldValue; } catch (LoadOrSaveException | DatabaseFileStructureException e) { + readLock.unlock(); throw new DatabaseException(e); + } finally { + readLock.unlock(); } - Storeable oldValue = get(key); - if (value != null) { - if (oldValue != null) { - changedKeys.put(key, null); - } - } else { - changedKeys.remove(key); - } - return oldValue; } @Override public int size() { + Lock readLock = lock.readLock(); + readLock.lock(); try { int deletedCount = 0; int addedCount = 0; - for (String key : changedKeys.keySet()) { - String value = changedKeys.get(key); + for (String key : changedKeys.get().keySet()) { + String value = changedKeys.get().get(key); if (value == null) { ++deletedCount; } else { @@ -113,37 +133,43 @@ public int size() { return stringTable.count() + addedCount - deletedCount; } catch (LoadOrSaveException | DatabaseFileStructureException e) { throw new DatabaseException(e); + } finally { + readLock.unlock(); } } @Override public int commit() throws IOException { + Lock writeLock = lock.writeLock(); + writeLock.lock(); int changes = changesCount(); try { - for (String key : changedKeys.keySet()) { - String value = changedKeys.get(key); + for (String key : changedKeys.get().keySet()) { + String value = changedKeys.get().get(key); if (value == null) { stringTable.remove(key); } else { stringTable.put(key, value); } } - changedKeys.clear(); + changedKeys.get().clear(); stringTable.save(); } catch (DatabaseFileStructureException | LoadOrSaveException e) { Database.throwIOException(e); + } finally { + writeLock.unlock(); } return changes; } public int changesCount() { - return changedKeys.size(); + return changedKeys.get().size(); } @Override public int rollback() { int changes = changesCount(); - changedKeys.clear(); + changedKeys.get().clear(); return changes; } @@ -154,12 +180,19 @@ public int getNumberOfUncommittedChanges() { @Override public String getName() { + Lock readLock = lock.readLock(); + readLock.lock(); + String name = this.name; + readLock.unlock(); return name; + } @Override public Storeable get(String key) { checkKeyNotNull(key); + Lock readLock = lock.readLock(); + readLock.lock(); try { String result = getString(key); if (result == null) { @@ -169,34 +202,52 @@ public Storeable get(String key) { } } catch (ParseException | LoadOrSaveException | DatabaseFileStructureException e) { throw new DatabaseException(e); + } finally { + readLock.unlock(); } } @Override public int getColumnsCount() { - return columnTypes.size(); + Lock readLock = lock.readLock(); + readLock.lock(); + int size = columnTypes.size(); + readLock.unlock(); + return size; } @Override public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { - return columnTypes.get(columnIndex); + Lock readLock = lock.readLock(); + readLock.lock(); + Class result = columnTypes.get(columnIndex); + readLock.unlock(); + return result; } public void drop() throws DatabaseFileStructureException, LoadOrSaveException { - Path signatureFile = database.getRootDirectoryPath().resolve(getName()). - resolve(Database.TABLE_SIGNATURE_FILE_NAME); - if (!signatureFile.toFile().delete()) { - throw new LoadOrSaveException("Can't delete signature file for table " + getName()); + Lock readLock = lock.readLock(); + readLock.lock(); + try { + Path signatureFile = database.getRootDirectoryPath().resolve(getName()). + resolve(Database.TABLE_SIGNATURE_FILE_NAME); + if (!signatureFile.toFile().delete()) { + throw new LoadOrSaveException("Can't delete signature file for table " + getName()); + } + stringTable.drop(); + } finally { + readLock.unlock(); } - stringTable.drop(); } @Override public List list() { + Lock readLock = lock.readLock(); + readLock.lock(); try { Set items = new TreeSet<>(stringTable.list()); - for (String key : changedKeys.keySet()) { - String value = changedKeys.get(key); + for (String key : changedKeys.get().keySet()) { + String value = changedKeys.get().get(key); if (value == null) { items.remove(key); } else { @@ -213,6 +264,8 @@ public void accept(String s) { return result; } catch (DatabaseFileStructureException | LoadOrSaveException e) { throw new DatabaseException(e); + } finally { + readLock.unlock(); } } } From 756c942226e6cffdeeb9f22e3376c4ca585dcc59 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Wed, 24 Dec 2014 11:40:35 +0300 Subject: [PATCH 31/36] Parallel tableProvider --- .../LebedevAleksey/storeable/Database.java | 167 +++++++++++------- 1 file changed, 99 insertions(+), 68 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index 80c743d34..b96fa9e0e 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -17,6 +17,9 @@ import java.nio.file.Path; import java.text.ParseException; import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.BiConsumer; public class Database implements TableProvider { @@ -43,44 +46,51 @@ public void accept(String alias, Class type) { private Path directoryPath; private Map tables = new HashMap<>(); + private ReadWriteLock lock = new ReentrantReadWriteLock(); public Database(String directory) throws IOException { - assertArgumentNotNull(directory, "directory"); - File root = new File(directory); - directoryPath = root.toPath(); - File[] tables = root.listFiles(); - for (File file : tables) { - if (file.isDirectory()) { - File signature = file.toPath().resolve(TABLE_SIGNATURE_FILE_NAME).toFile(); - String tablename = file.getName(); - if (signature.exists() && signature.isFile()) { - String signatureString; - try (FileInputStream stream = new FileInputStream(signature.getAbsolutePath())) { - try (DataInputStream signaturedata = new DataInputStream(stream)) { - signatureString = signaturedata.readUTF(); + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + assertArgumentNotNull(directory, "directory"); + File root = new File(directory); + directoryPath = root.toPath(); + File[] tables = root.listFiles(); + for (File file : tables) { + if (file.isDirectory()) { + File signature = file.toPath().resolve(TABLE_SIGNATURE_FILE_NAME).toFile(); + String tablename = file.getName(); + if (signature.exists() && signature.isFile()) { + String signatureString; + try (FileInputStream stream = new FileInputStream(signature.getAbsolutePath())) { + try (DataInputStream signaturedata = new DataInputStream(stream)) { + signatureString = signaturedata.readUTF(); + } } - } - String[] tokens = signatureString.split(" "); - List> types = new ArrayList<>(); - if (signatureString.length() != 0) { - for (String item : tokens) { - Class type = stringTypesMap.get(item); - if (type == null) { - throw new IOException("Wrong type name in signature of table " + tablename); - } else { - types.add(type); + String[] tokens = signatureString.split(" "); + List> types = new ArrayList<>(); + if (signatureString.length() != 0) { + for (String item : tokens) { + Class type = stringTypesMap.get(item); + if (type == null) { + throw new IOException("Wrong type name in signature of table " + tablename); + } else { + types.add(type); + } } } + StoreableTable table = generateTable(tablename, types); + this.tables.put(tablename, table); + } else { + throw new IOException("Where is not signature file in table " + tablename); } - StoreableTable table = generateTable(tablename, types); - this.tables.put(tablename, table); } else { - throw new IOException("Where is not signature file in table " + tablename); + fileFoundInRootDirectory(file); } - } else { - fileFoundInRootDirectory(file); } + } finally { + writeLock.unlock(); } } @@ -116,47 +126,57 @@ protected void fileFoundInRootDirectory(File file) throws IOException { @Override public Table getTable(String name) { assertArgumentNotNull(name, "name"); - return tables.get(name); + Lock readLock = lock.readLock(); + readLock.lock(); + StoreableTable table = tables.get(name); + readLock.unlock(); + return table; } @Override public Table createTable(String name, List> columnTypes) throws IOException { assertArgumentNotNull(name, "name"); assertArgumentNotNull(columnTypes, "columnTypes"); - Table checkExists = getTable(name); - if (checkExists == null) { - Path rootDirectoryPath = getRootDirectoryPath(); - Path path = rootDirectoryPath.resolve(name); - if (path.startsWith(rootDirectoryPath) && path.getParent().equals(rootDirectoryPath)) { - if (columnTypes == null) { - throw new IllegalArgumentException("Argument \"columnTypes\" is null."); - } - String tableSignature = createTableSignature(columnTypes); - try { - Files.createDirectory(path); - try (FileOutputStream stream = new FileOutputStream(path.resolve( - TABLE_SIGNATURE_FILE_NAME).toString())) { - try (DataOutputStream dataStream = new DataOutputStream(stream)) { - dataStream.writeUTF(tableSignature); - dataStream.flush(); - } + Lock writeLock = lock.writeLock(); + writeLock.lock(); + try { + Table checkExists = getTable(name); + if (checkExists == null) { + Path rootDirectoryPath = getRootDirectoryPath(); + Path path = rootDirectoryPath.resolve(name); + if (path.startsWith(rootDirectoryPath) && path.getParent().equals(rootDirectoryPath)) { + if (columnTypes == null) { + throw new IllegalArgumentException("Argument \"columnTypes\" is null."); } - } catch (Exception ex) { + String tableSignature = createTableSignature(columnTypes); try { - Files.delete(path); - } catch (Throwable suppressed) { - ex.addSuppressed(suppressed); + Files.createDirectory(path); + try (FileOutputStream stream = new FileOutputStream(path.resolve( + TABLE_SIGNATURE_FILE_NAME).toString())) { + try (DataOutputStream dataStream = new DataOutputStream(stream)) { + dataStream.writeUTF(tableSignature); + dataStream.flush(); + } + } + } catch (Exception ex) { + try { + Files.delete(path); + } catch (Throwable suppressed) { + ex.addSuppressed(suppressed); + } + throw ex; } - throw ex; + StoreableTable table = generateTable(name, columnTypes); + tables.put(name, table); + return table; + } else { + throw new IllegalArgumentException(INCORRECT_NAME_OF_TABLES); } - StoreableTable table = generateTable(name, columnTypes); - tables.put(name, table); - return table; } else { - throw new IllegalArgumentException(INCORRECT_NAME_OF_TABLES); + return null; } - } else { - return null; + } finally { + writeLock.unlock(); } } @@ -188,16 +208,22 @@ private StoreableTable generateTable(String name, List> types) { @Override public void removeTable(String name) throws IOException { assertArgumentNotNull(name, "name"); - StoreableTable table = (StoreableTable) getTable(name); - if (table == null) { - throw new IllegalStateException("There is no table with name \"" + name + "\""); - } + Lock writeLock = lock.writeLock(); + writeLock.lock(); try { - table.drop(); - } catch (DatabaseFileStructureException | LoadOrSaveException e) { - throwIOException(e); + StoreableTable table = (StoreableTable) getTable(name); + if (table == null) { + throw new IllegalStateException("There is no table with name \"" + name + "\""); + } + try { + table.drop(); + } catch (DatabaseFileStructureException | LoadOrSaveException e) { + throwIOException(e); + } + tables.remove(name); + } finally { + writeLock.unlock(); } - tables.remove(name); } @Override @@ -300,10 +326,11 @@ public Storeable createFor(Table table, List values) throws ColumnFormatExcep @Override public List getTableNames() { + Lock readLock = lock.readLock(); + readLock.lock(); final List result = new ArrayList<>(tables.size()); - tables.keySet().forEach((String s) -> { - result.add(s); - }); + tables.keySet().forEach((String s) -> result.add(s)); + readLock.unlock(); return result; } @@ -315,6 +342,8 @@ private void assertArgumentNotNull(Object argument, String name) { public List> listTables() throws IOException { final List> result = new ArrayList<>(tables.size()); + Lock readLock = lock.readLock(); + readLock.lock(); try { tables.forEach(new BiConsumer() { @Override @@ -324,6 +353,8 @@ public void accept(String s, StoreableTable table) { }); } catch (DatabaseException e) { throwIOException(e.getCause()); + }finally { + readLock.unlock(); } return result; } From 64cc20d5851a9e00297d8d947f6fd13275fc61d3 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Wed, 24 Dec 2014 11:49:35 +0300 Subject: [PATCH 32/36] Parallel working in table optimisations --- .../storeable/StoreableTable.java | 78 +++++++++++-------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java index d02db8beb..9e3569580 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/StoreableTable.java @@ -30,10 +30,13 @@ protected Map initialValue() { private ReadWriteLock lock = new ReentrantReadWriteLock(true); public StoreableTable(String name, Database databaseParent, List> types) { + Lock writeLock = lock.writeLock(); + writeLock.lock(); this.name = name; database = databaseParent; columnTypes = types; stringTable = new Table(name, databaseParent.getRootDirectoryPath()); + writeLock.unlock(); } private void checkKeyNotNull(String key) { @@ -72,20 +75,23 @@ public Storeable put(String key, Storeable value) throws ColumnFormatException { checkKeyValueNotNull(key, value); Lock readLock = lock.readLock(); readLock.lock(); + String result; try { - String result = putStrings(key, database.serialize(this, value)); - if (result == null) { - return null; - } else { - return database.deserialize(this, result); - } + result = putStrings(key, database.serialize(this, value)); } catch (DatabaseFileStructureException | LoadOrSaveException e) { throw new DatabaseException(e); - } catch (ParseException e) { - throw new ColumnFormatException(e); } finally { readLock.unlock(); } + if (result == null) { + return null; + } else { + try { + return database.deserialize(this, result); + } catch (ParseException e) { + throw new ColumnFormatException(e); + } + } } @Override @@ -116,7 +122,6 @@ public Storeable remove(String key) { @Override public int size() { Lock readLock = lock.readLock(); - readLock.lock(); try { int deletedCount = 0; int addedCount = 0; @@ -130,6 +135,7 @@ public int size() { } } } + readLock.lock(); return stringTable.count() + addedCount - deletedCount; } catch (LoadOrSaveException | DatabaseFileStructureException e) { throw new DatabaseException(e); @@ -193,17 +199,22 @@ public Storeable get(String key) { checkKeyNotNull(key); Lock readLock = lock.readLock(); readLock.lock(); + String result; + try { + result = getString(key); + } catch (LoadOrSaveException | DatabaseFileStructureException e) { + throw new DatabaseException(e); + } finally { + readLock.unlock(); + } try { - String result = getString(key); if (result == null) { return null; } else { return database.deserialize(this, result); } - } catch (ParseException | LoadOrSaveException | DatabaseFileStructureException e) { + } catch (ParseException e) { throw new DatabaseException(e); - } finally { - readLock.unlock(); } } @@ -226,8 +237,8 @@ public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException } public void drop() throws DatabaseFileStructureException, LoadOrSaveException { - Lock readLock = lock.readLock(); - readLock.lock(); + Lock writeLock = lock.writeLock(); + writeLock.lock(); try { Path signatureFile = database.getRootDirectoryPath().resolve(getName()). resolve(Database.TABLE_SIGNATURE_FILE_NAME); @@ -236,7 +247,7 @@ public void drop() throws DatabaseFileStructureException, LoadOrSaveException { } stringTable.drop(); } finally { - readLock.unlock(); + writeLock.unlock(); } } @@ -244,28 +255,29 @@ public void drop() throws DatabaseFileStructureException, LoadOrSaveException { public List list() { Lock readLock = lock.readLock(); readLock.lock(); + Set items; try { - Set items = new TreeSet<>(stringTable.list()); - for (String key : changedKeys.get().keySet()) { - String value = changedKeys.get().get(key); - if (value == null) { - items.remove(key); - } else { - items.add(key); - } - } - final ArrayList result = new ArrayList<>(items.size()); - items.forEach(new Consumer() { - @Override - public void accept(String s) { - result.add(s); - } - }); - return result; + items = new TreeSet<>(stringTable.list()); } catch (DatabaseFileStructureException | LoadOrSaveException e) { throw new DatabaseException(e); } finally { readLock.unlock(); } + for (String key : changedKeys.get().keySet()) { + String value = changedKeys.get().get(key); + if (value == null) { + items.remove(key); + } else { + items.add(key); + } + } + final ArrayList result = new ArrayList<>(items.size()); + items.forEach(new Consumer() { + @Override + public void accept(String s) { + result.add(s); + } + }); + return result; } } From 1948c150f28d0ba7f06f726a322e8eaa9f9c80a4 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Wed, 24 Dec 2014 12:24:46 +0300 Subject: [PATCH 33/36] Tests for parllel work --- .../storeable/tests/ParallelTest.java | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java new file mode 100644 index 000000000..40470dc02 --- /dev/null +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java @@ -0,0 +1,118 @@ +package ru.fizteh.fivt.students.LebedevAleksey.storeable.tests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.students.LebedevAleksey.storeable.DatabaseFactory; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class ParallelTest { + final AtomicInteger numberOfAction = new AtomicInteger(0); + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + private File dbPath; + private TableProvider database; + private Table table; + private final ThreadLocal>> columnTypes = new ThreadLocal>>() { + @Override + protected List> initialValue() { + return Arrays.asList(Integer.class); + } + }; + + @Before + public void setUp() throws IOException { + dbPath = temporaryFolder.newFolder("db"); + database = new DatabaseFactory().create(dbPath.getAbsolutePath()); + table = database.createTable("table", columnTypes.get()); + } + + @Test + public void checkParallelWork() throws IOException, InterruptedException { + Storeable value = database.createFor(table); + value.setColumnAt(0, 5); + table.put("a", value); + value.setColumnAt(0, 7); + table.put("b", value); + table.commit(); + Thread t1 = new Thread(() -> { + try { + table.put("c", database.createFor(table)); + Storeable a = table.get("a"); + a.setColumnAt(0, 2); + table.put("a", a); + numberOfAction.incrementAndGet(); + waitFor(3); + Assert.assertEquals(3, table.size()); + Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); + Assert.assertEquals((Integer) 7, table.get("b").getIntAt(0)); + Assert.assertEquals(null, table.get("c").getIntAt(0)); + numberOfAction.incrementAndGet(); + waitFor(5); + table.commit(); + numberOfAction.incrementAndGet(); + waitFor(7); + Assert.assertEquals(2, table.size()); + Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); + Assert.assertEquals(null, table.get("c").getIntAt(0)); + Assert.assertNotNull(database.getTable("t2")); + Assert.assertNull(database.createTable("t2", columnTypes.get())); + database.removeTable("t2"); + numberOfAction.incrementAndGet(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + Thread t2 = new Thread(() -> { + try { + table.remove("b"); + numberOfAction.incrementAndGet(); + waitFor(3); + Assert.assertEquals(1, table.size()); + Assert.assertEquals((Integer) 5, table.get("a").getIntAt(0)); + numberOfAction.incrementAndGet(); + waitFor(6); + Assert.assertEquals(2, table.size()); + Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); + Assert.assertEquals(null, table.get("c").getIntAt(0)); + table.commit(); + database.createTable("t2", columnTypes.get()); + numberOfAction.incrementAndGet(); + waitFor(8); + Assert.assertNull(database.getTable("t2")); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + t1.start(); + t2.start(); + waitFor(2); + Assert.assertEquals(2, table.size()); + Assert.assertEquals((Integer) 5, table.get("a").getIntAt(0)); + Assert.assertEquals((Integer) 7, table.get("b").getIntAt(0)); + numberOfAction.incrementAndGet(); + t1.join(); + t2.join(); + } + + private void waitFor(int value) { + try { + while (numberOfAction.get() != value) { + //System.out.println(numberOfAction.get() + " " + value); + Thread.currentThread().sleep(5); + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} From 9cd824dddf186f4d3316e0df63a5a693e0731ba3 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Wed, 24 Dec 2014 12:37:50 +0300 Subject: [PATCH 34/36] Remove commented code --- .../students/LebedevAleksey/storeable/tests/ParallelTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java index 40470dc02..1620c915e 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java @@ -108,7 +108,6 @@ public void checkParallelWork() throws IOException, InterruptedException { private void waitFor(int value) { try { while (numberOfAction.get() != value) { - //System.out.println(numberOfAction.get() + " " + value); Thread.currentThread().sleep(5); } } catch (InterruptedException e) { From c606b3c4b7c6ad6214891e248773ea875b95f58a Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Wed, 24 Dec 2014 13:30:26 +0300 Subject: [PATCH 35/36] checkstyle fix --- .../fizteh/fivt/students/LebedevAleksey/storeable/Database.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java index b96fa9e0e..a395f4f86 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/Database.java @@ -353,7 +353,7 @@ public void accept(String s, StoreableTable table) { }); } catch (DatabaseException e) { throwIOException(e.getCause()); - }finally { + } finally { readLock.unlock(); } return result; From aaa94a3a9bc82308961ebf7857f5866ebd815670 Mon Sep 17 00:00:00 2001 From: Lebedev Aleksey Date: Wed, 24 Dec 2014 19:04:52 +0300 Subject: [PATCH 36/36] Fix bug in test --- .../storeable/tests/ParallelTest.java | 149 ++++++++++-------- 1 file changed, 83 insertions(+), 66 deletions(-) diff --git a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java index 1620c915e..d43589539 100644 --- a/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java +++ b/src/ru/fizteh/fivt/students/LebedevAleksey/storeable/tests/ParallelTest.java @@ -37,78 +37,95 @@ public void setUp() throws IOException { table = database.createTable("table", columnTypes.get()); } - @Test + Object lock = new Object(); + + @Test(timeout = 30000) public void checkParallelWork() throws IOException, InterruptedException { - Storeable value = database.createFor(table); - value.setColumnAt(0, 5); - table.put("a", value); - value.setColumnAt(0, 7); - table.put("b", value); - table.commit(); - Thread t1 = new Thread(() -> { - try { - table.put("c", database.createFor(table)); - Storeable a = table.get("a"); - a.setColumnAt(0, 2); - table.put("a", a); - numberOfAction.incrementAndGet(); - waitFor(3); - Assert.assertEquals(3, table.size()); - Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); - Assert.assertEquals((Integer) 7, table.get("b").getIntAt(0)); - Assert.assertEquals(null, table.get("c").getIntAt(0)); - numberOfAction.incrementAndGet(); - waitFor(5); - table.commit(); - numberOfAction.incrementAndGet(); - waitFor(7); - Assert.assertEquals(2, table.size()); - Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); - Assert.assertEquals(null, table.get("c").getIntAt(0)); - Assert.assertNotNull(database.getTable("t2")); - Assert.assertNull(database.createTable("t2", columnTypes.get())); - database.removeTable("t2"); - numberOfAction.incrementAndGet(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - Thread t2 = new Thread(() -> { - try { - table.remove("b"); - numberOfAction.incrementAndGet(); - waitFor(3); - Assert.assertEquals(1, table.size()); - Assert.assertEquals((Integer) 5, table.get("a").getIntAt(0)); - numberOfAction.incrementAndGet(); - waitFor(6); - Assert.assertEquals(2, table.size()); - Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); - Assert.assertEquals(null, table.get("c").getIntAt(0)); - table.commit(); - database.createTable("t2", columnTypes.get()); - numberOfAction.incrementAndGet(); - waitFor(8); - Assert.assertNull(database.getTable("t2")); - } catch (Exception e) { - throw new RuntimeException(e); - } - }); - t1.start(); - t2.start(); - waitFor(2); - Assert.assertEquals(2, table.size()); - Assert.assertEquals((Integer) 5, table.get("a").getIntAt(0)); - Assert.assertEquals((Integer) 7, table.get("b").getIntAt(0)); + for (int i = 0; i < 15; i++) { + numberOfAction.set(0); + Storeable value = database.createFor(table); + value.setColumnAt(0, 5); + table.put("a", value); + value.setColumnAt(0, 7); + table.put("b", value); + table.commit(); + Thread t1 = new Thread(() -> { + try { + table.put("c", database.createFor(table)); + Storeable a = table.get("a"); + a.setColumnAt(0, 2); + table.put("a", a); + nextAction(); + waitFor(3); + Assert.assertEquals(3, table.size()); + Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); + Assert.assertEquals((Integer) 7, table.get("b").getIntAt(0)); + Assert.assertEquals(null, table.get("c").getIntAt(0)); + nextAction(); + waitFor(5); + table.commit(); + nextAction(); + waitFor(7); + Assert.assertEquals(2, table.size()); + Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); + Assert.assertEquals(null, table.get("c").getIntAt(0)); + Assert.assertNotNull(database.getTable("t2")); + Assert.assertNull(database.createTable("t2", columnTypes.get())); + database.removeTable("t2"); + nextAction(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + Thread t2 = new Thread(() -> { + try { + table.remove("b"); + nextAction(); + waitFor(3); + Assert.assertEquals(1, table.size()); + Assert.assertEquals((Integer) 5, table.get("a").getIntAt(0)); + nextAction(); + waitFor(6); + Assert.assertEquals(2, table.size()); + Assert.assertEquals((Integer) 2, table.get("a").getIntAt(0)); + Assert.assertEquals(null, table.get("c").getIntAt(0)); + table.commit(); + database.createTable("t2", columnTypes.get()); + nextAction(); + waitFor(8); + Assert.assertNull(database.getTable("t2")); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + t1.start(); + t2.start(); + waitFor(2); + Assert.assertEquals(2, table.size()); + Assert.assertEquals((Integer) 5, table.get("a").getIntAt(0)); + Assert.assertEquals((Integer) 7, table.get("b").getIntAt(0)); + nextAction(); + t1.join(); + t2.join(); + table.remove("c"); + table.remove("b"); + table.commit(); + } + } + + private void nextAction() { numberOfAction.incrementAndGet(); - t1.join(); - t2.join(); + synchronized (lock) { + lock.notifyAll(); + } } private void waitFor(int value) { try { - while (numberOfAction.get() != value) { - Thread.currentThread().sleep(5); + synchronized (lock) { + while (numberOfAction.get() < value) { + lock.wait(); + } } } catch (InterruptedException e) { throw new RuntimeException(e);