diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Commands.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Commands.java new file mode 100644 index 000000000..cd74dfe24 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Commands.java @@ -0,0 +1,11 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell; + + +public interface Commands { + + String getCommandName(); + + int getArgumentQuantity(); + + void implement(String args, State state) throws SomethingIsWrongException; +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Parser.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Parser.java new file mode 100644 index 000000000..294da1161 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Parser.java @@ -0,0 +1,45 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Parser { + + + public static ArrayList parseCommandArgs(String params) { + ArrayList matchList = new ArrayList(); + Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'"); + Matcher regexMatcher = regex.matcher(params); + while (regexMatcher.find()) { + String param = regexMatcher.group().replaceAll("\"?([~\"]*)\"?", "$1"); + matchList.add(param); + } + return matchList; + } + + public static String getName(String command) { + int spiltIndex = command.indexOf(' '); + if (spiltIndex == -1) { + return command; + } else { + return command.substring(0, spiltIndex); + } + } + + public static String[] parseFullCommand(String commands) { + String[] commandArray = commands.split(";"); + for (int i = 0; i < commandArray.length; ++i) { + commandArray[i] = commandArray[i].trim(); + } + return commandArray; + } + public static String getParameters(String command) { + int spiltIndex = command.indexOf(' '); + if (spiltIndex == -1) { + return ""; + } else { + return command.substring(spiltIndex + 1); + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Shell.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Shell.java new file mode 100644 index 000000000..9b1380f2c --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/Shell.java @@ -0,0 +1,90 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; + + + + +public class Shell { + private final Map availableCommands; + private static final String GREETING = "$ "; + State state; + + + public Shell(Set> commands) { + Map tempCommands = new HashMap(); + for (Commands temp : commands) { + tempCommands.put(temp.getCommandName(), temp); + } + availableCommands = Collections.unmodifiableMap(tempCommands); + } + + public void setShellState(State state) { + this.state = state; + } + + private void runCommand(String[] data, State state) throws SomethingIsWrongException { + for (String command : data) { + if (data[0].length() == 0) { + throw new SomethingIsWrongException("Empty string."); + } + String name = Parser.getName(command); + Commands usedOne = availableCommands.get(name); + String args = Parser.getParameters(command); + if (usedOne == null) { + throw new SomethingIsWrongException("Unknown command."); + } + usedOne.implement(args, state); + } + } + + public void consoleWay(State state) { + Scanner forInput = new Scanner(System.in); + while (!Thread.currentThread().isInterrupted()) { + System.out.print(GREETING); + try { + String temp = forInput.nextLine(); + String[] commands = Parser.parseFullCommand(temp); + runCommand(commands, state); + } catch (SomethingIsWrongException exc) { + if (exc.getMessage().equals("EXIT")) { + forInput.close(); + System.exit(0); + } else { + System.err.println(exc.getMessage()); + } + } catch (IllegalArgumentException | IllegalStateException e) { + System.err.println(e.getMessage()); + } + } + forInput.close(); + } + + + public void run(String[] args, Shell shell) { + if (args.length != 0) { + String arg = UtilMethods.uniteItems(Arrays.asList(args), " "); + String[] commands = Parser.parseFullCommand(arg); + try { + runCommand(commands, state); + } catch (SomethingIsWrongException exc) { + if (exc.getMessage().equals("EXIT")) { + System.exit(0); + } else { + System.err.println(exc.getMessage()); + System.exit(-1); + } + } + } else { + consoleWay(state); + } + System.exit(0); + } + + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/ShellState.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/ShellState.java new file mode 100644 index 000000000..893f4b1c2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/ShellState.java @@ -0,0 +1,14 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell; + +public class ShellState { + private String currentDirectory; + public ShellState(String currentDirectory) { + this.currentDirectory = currentDirectory; + } + public String getCurDir() { + return currentDirectory; + } + void changeCurDir(String newCurDir) { + currentDirectory = newCurDir; + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/SomeCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/SomeCommand.java new file mode 100644 index 000000000..8e1592e66 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/SomeCommand.java @@ -0,0 +1,4 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell; + +public abstract class SomeCommand implements Commands { +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/SomethingIsWrongException.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/SomethingIsWrongException.java new file mode 100644 index 000000000..cfe244972 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/SomethingIsWrongException.java @@ -0,0 +1,20 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell; + + +public final class SomethingIsWrongException extends Exception { + public SomethingIsWrongException() { + super(); + } + + public SomethingIsWrongException(String message) { + super(message); + } + + public SomethingIsWrongException(String message, Throwable cause) { + super(message, cause); + } + + public SomethingIsWrongException(Throwable cause) { + super(cause); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/UtilMethods.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/UtilMethods.java new file mode 100644 index 000000000..d40ecdaf8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/Shell/UtilMethods.java @@ -0,0 +1,100 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Collection; + +public class UtilMethods { + public static final String ENCODING = "UTF-8"; + public static String uniteItems(Collection items, String separator) { + boolean isFirstIteration = true; + StringBuilder joinBuilder = new StringBuilder(); + for (Object item: items) { + if (isFirstIteration) { + isFirstIteration = false; + } else { + joinBuilder.append(separator); + } + joinBuilder.append(item.toString()); + } + return joinBuilder.toString(); + } + + public static void closeCalm(Closeable something) { + try { + if (something != null) { + something.close(); + } + } catch (IOException e) { + System.err.println("Error aquired while trying to close " + e.getMessage()); + } + } + + public static void copyFile(File source, File dirDestination) throws SomethingIsWrongException { + File copy = new File(dirDestination, source.getName()); + FileInputStream ofOriginal = null; + FileOutputStream ofCopy = null; + try { + copy.createNewFile(); + ofOriginal = new FileInputStream(source); + ofCopy = new FileOutputStream(copy); + byte[] buf = new byte[4096]; //size of reading = 4kB + int read = ofOriginal.read(buf); + while (read > 0) { + ofCopy.write(buf, 0, read); + read = ofOriginal.read(buf); + } + } catch (FileNotFoundException e) { + throw new SomethingIsWrongException("This file or directory doesn't exist yet. " + e.getMessage()); + } catch (IOException e) { + throw new SomethingIsWrongException("Error while writing/reading file. " + e.getMessage()); + } finally { + closeCalm(ofOriginal); + closeCalm(ofCopy); + } + } + + public static File getAbsoluteName(String fileName, ShellState state) { + File file = new File(fileName); + + if (!file.isAbsolute()) { + file = new File(state.getCurDir(), fileName); + } + return file; + } + public static byte[] getBytes(String string, String encoding) throws SomethingIsWrongException { + byte[] bytes = null; + try { + bytes = string.getBytes(encoding); + } catch (UnsupportedEncodingException e) { + throw new SomethingIsWrongException("Unable to convert string to bytes of this type: " + e.getMessage()); + } + return bytes; + } + + public static byte[] bytesToArray(ByteArrayOutputStream bytes) { + byte[] result = new byte[bytes.size()]; + result = bytes.toByteArray(); + return result; + } + + public static boolean doesExist(String path) { + File file = new File(path); + return file.exists(); + } + + public static int countBytes(String string, String encoding) { + try { + byte[] bytes = string.getBytes(encoding); + return bytes.length; + } catch (Exception e) { + return 0; + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/CommitCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/CommitCommand.java new file mode 100644 index 000000000..dd4152169 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/CommitCommand.java @@ -0,0 +1,24 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + +public class CommitCommand extends SomeCommand { + + public String getCommandName() { + return "commit"; + } + + public int getArgumentQuantity() { + return 0; + } + + public void implement(String args, State state) + throws SomethingIsWrongException { + if (state.getTable() == null) { + throw new SomethingIsWrongException("no table"); + } + System.out.println(state.commit()); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ExitCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ExitCommand.java new file mode 100644 index 000000000..6313c8a25 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ExitCommand.java @@ -0,0 +1,27 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + +public class ExitCommand extends SomeCommand { + + public String getCommandName() { + return "exit"; + } + + public int getArgumentQuantity() { + return 0; + } + + public void implement(String args, State state) + throws SomethingIsWrongException { + /*MyTable temp = (MyTable) state.getTable();*/ + if (state.getTable() != null /*&& !temp.getAutoCommit()*/) { + state.rollback(); + }/* else if (temp.getAutoCommit() && state.getTable() != null) { + state.commit(); + }*/ + throw new SomethingIsWrongException("EXIT"); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapReadingUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapReadingUtils.java new file mode 100644 index 000000000..2b67222c6 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapReadingUtils.java @@ -0,0 +1,112 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.EOFException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + + +public class FileMapReadingUtils implements Closeable{ + + protected static RandomAccessFile tempFile; + private static int valueShift = -1; + + public FileMapReadingUtils(String pathToFile) throws IOException { + try { + tempFile = new RandomAccessFile(pathToFile, "r"); + } catch (FileNotFoundException e) { + tempFile = null; + valueShift = 0; + return; + } + if (tempFile.length() == 0) { + throw new IOException("empty file"); + } + skipKey(); + valueShift = readOffset(); + tempFile.seek(0); + } + + public String readKey() throws IOException { + if (tempFile.getFilePointer() >= valueShift) { + return null; + } + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte b = tempFile.readByte(); + while (b != 0) { + bytes.write(b); + b = tempFile.readByte(); + } + return new String(bytes.toByteArray(), GlobalUtils.ENCODING); + } + + public String readValue() throws IOException { + int offset = readOffset(); + int nextOffset = readNextOffset(); + long currentOffset; + currentOffset = tempFile.getFilePointer(); + tempFile.seek(offset); + int valueLength = nextOffset - offset; + byte[] bytes = new byte[valueLength]; + tempFile.read(bytes, 0, valueLength); + tempFile.seek(currentOffset); + return new String(bytes, GlobalUtils.ENCODING); + + } + + public static void scanFromDisk(String file, TableBuilder builder) throws IOException { + if (!GlobalUtils.doesExist(file)) { + throw new IOException("didn't exist"); + } + FileMapReadingUtils read = new FileMapReadingUtils(file); + while (!read.endOfFile()) { + String key = read.readKey(); + String value = read.readValue(); + builder.put(key, value); + } + read.close(); + } + + public boolean endOfFile() throws IOException { + if (tempFile == null) { + return true; + } + boolean result = true; + try { + result = (tempFile.getFilePointer() == valueShift); + } catch (EOFException e) { + System.err.println("Reached end of file"); + } + return result; + } + + + private int readNextOffset() throws IOException { + int nextOffset = 0; + long currentOffset = tempFile.getFilePointer(); + if (readKey() == null) { + nextOffset = (int) tempFile.length(); + } else { + nextOffset = readOffset(); + } + tempFile.seek(currentOffset); + return nextOffset; + } + + private void skipKey() throws IOException { + byte b; + do { + b = tempFile.readByte(); + } while(b != 0); + } + + private int readOffset() throws IOException { + return tempFile.readInt(); + } + + public void close() { + GlobalUtils.closeCalm(tempFile); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapShellState.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapShellState.java new file mode 100644 index 000000000..aa4fa2343 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapShellState.java @@ -0,0 +1,6 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; +import ru.fizteh.fivt.storage.strings.Table; + +public class FileMapShellState { + public Table table = null; + } diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapShellStateInterface.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapShellStateInterface.java new file mode 100644 index 000000000..db1836208 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapShellStateInterface.java @@ -0,0 +1,27 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + + +public interface FileMapShellStateInterface { + + Value put(Key key, Value value); + + Value get(Key key); + + int commit(); + + int rollback(); + + int size(); + + Value remove(Key key); + + Table getTable(); + + String keyToString(Key key); + + String valueToString(Value value); + + Key parseKey(String key); + + Value parseValue(String value); +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapWritingUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapWritingUtils.java new file mode 100644 index 000000000..5d46c5022 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/FileMapWritingUtils.java @@ -0,0 +1,65 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.io.Closeable; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; +import java.util.Set; + + + +public class FileMapWritingUtils implements Closeable { + + protected static RandomAccessFile dataFile; + public FileMapWritingUtils(String filePath) throws IOException { + try { + dataFile = new RandomAccessFile(filePath, "rw"); + } catch (FileNotFoundException e) { + throw new IOException(String.format("error while creating file: '%s'", filePath)); + } + try { + dataFile.setLength(0); + } catch (IOException e) { + throw new IOException("Error aqcuired while resizing a file " + e.getMessage()); + } + } + + public void writeKey(String key) throws IOException { + byte[] bytes = GlobalUtils.getBytes(key, GlobalUtils.ENCODING); + dataFile.write(bytes); + dataFile.writeByte(0); + } + + public static void writeOnDisk(Set keys, String file, TableBuilder builder) throws IOException { + FileMapWritingUtils write = new FileMapWritingUtils(file); + int shift = GlobalUtils.getKeysLength(keys, GlobalUtils.ENCODING); + for (String key : keys) { + write.writeKey(key); + write.writeOffset(shift); + shift += GlobalUtils.countBytes(builder.get(key), GlobalUtils.ENCODING); + } + for (String key : keys) { + write.writeValue(builder.get(key)); + } + write.close(); + } + + public void writeOffset(int offset) throws IOException { + dataFile.writeInt(offset); + } + + public void writeValue(String value) throws IOException { + try { + byte[] bytes = value.getBytes(GlobalUtils.ENCODING); + dataFile.write(bytes); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Unrecognized encoding"); + } + } + + public void close() { + GlobalUtils.closeCalm(dataFile); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/GetCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/GetCommand.java new file mode 100644 index 000000000..d72227cc4 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/GetCommand.java @@ -0,0 +1,40 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.util.ArrayList; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Parser; + +public class GetCommand> + extends SomeCommand { + + public String getCommandName() { + return "get"; + } + + public int getArgumentQuantity() { + return 1; + } + + public void implement(String args, State state) + throws SomethingIsWrongException { + if (state.getTable() == null) { + throw new SomethingIsWrongException("no table"); + } + ArrayList parameters = Parser.parseCommandArgs(args); + if (parameters.size() != 1) { + throw new IllegalArgumentException("Use 1 argument for this operation"); + } + Key key = state.parseKey(parameters.get(0)); + Value value = state.get(key); + if (value == null) { + System.out.println("not found"); + } else { + System.out.println("found\n" + state.valueToString(value)); + } + + } + + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/GlobalUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/GlobalUtils.java new file mode 100644 index 000000000..e534b10b9 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/GlobalUtils.java @@ -0,0 +1,149 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.ShellState; + +public class GlobalUtils { + public static final Charset ENCODING = StandardCharsets.UTF_8; + public static final int DIR_QUANTITY = 16; + public static final int FILES_PER_DIR = 16; + private static final Pattern DIR_PATTERN = Pattern.compile("([^\\.]+).dir"); + private static final Pattern FILE_PATTERN = Pattern.compile("([^\\.]+).dat"); + + public static String uniteItems(Collection items, String separator) { + boolean isFirstIteration = true; + StringBuilder joinBuilder = new StringBuilder(); + for (Object item: items) { + if (isFirstIteration) { + isFirstIteration = false; + } else { + joinBuilder.append(separator); + } + joinBuilder.append(item.toString()); + } + return joinBuilder.toString(); + } + + public static void closeCalm(Closeable something) { + try { + if (something != null) { + something.close(); + } + } catch (IOException e) { + System.err.println("Error aqcuired while trying to close " + e.getMessage()); + } + } + + public static File getAbsoluteName(String fileName, ShellState state) { + File file = new File(fileName); + + if (!file.isAbsolute()) { + file = new File(state.getCurDir(), fileName); + } + return file; + } + public static byte[] getBytes(String string, Charset encoding) { + byte[] bytes = null; + bytes = string.getBytes(encoding); + return bytes; + } + + public static byte[] bytesToArray(ByteArrayOutputStream bytes) { + byte[] result = new byte[bytes.size()]; + result = bytes.toByteArray(); + return result; + } + + public static boolean doesExist(String path) { + File file = new File(path); + return file.exists(); + } + + public static int countBytes(String string, Charset encoding) { + byte[] bytes = string.getBytes(encoding); + return bytes.length; + } + + public static int getKeysLength(Set keys, Charset charset) { + int keysLength = 0; + for (final String key : keys) { + int keyLength = countBytes(key, charset); + keysLength += keyLength + 5; + } + return keysLength; + } + + public static boolean compare(Object key1, Object key2) { + if (key1 == null && key2 == null) { + return true; + } else if (key1 == null || key2 == null) { + return false; + } else { + return key1.equals(key2); + } + } + + public static void deleteFile(File fileToDelete) { + if (!fileToDelete.exists()) { + return; + } + if (fileToDelete.isDirectory()) { + for (final File file : fileToDelete.listFiles()) { + deleteFile(file); + } + } + fileToDelete.delete(); + } + + public static int parseDirNumber(File dir) { + String name = dir.getName(); + Matcher matcher = DIR_PATTERN.matcher(name); + if (matcher.matches()) { + return Integer.parseInt(matcher.group(1)); + } + throw new IllegalArgumentException("incorrect dir name"); + } + + public static int parseFileNumber(File file) { + String name = file.getName(); + Matcher matcher = FILE_PATTERN.matcher(name); + if (matcher.matches()) { + return Integer.parseInt(matcher.group(1)); + } + throw new IllegalArgumentException("incorrect file name"); + } + + public static void checkKeyPlacement(String key, int currentDir, int currentFile) { + if (currentDir != getDirNumber(key) || currentFile != getFileNumber(key)) { + throw new IllegalArgumentException("invalid key placement"); + } + } + + public static String parseTableName(String params) { + int index = params.indexOf(' '); + if (index == -1) { + return params; + } + return params.substring(0, index); + } + + public static int getDirNumber(String key) { + byte[] bytes = GlobalUtils.getBytes(key, GlobalUtils.ENCODING); + return Math.abs(bytes[0] % DIR_QUANTITY); + } + + public static int getFileNumber(String key) { + byte[] bytes = GlobalUtils.getBytes(key, GlobalUtils.ENCODING); + return Math.abs(bytes[0] / DIR_QUANTITY % FILES_PER_DIR); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ListCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ListCommand.java new file mode 100644 index 000000000..a0ecaed49 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ListCommand.java @@ -0,0 +1,44 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.util.ArrayList; +import java.util.List; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.*; + +public class ListCommand implements Commands { + + public String getCommandName() { + return "list"; + } + + public int getArgumentQuantity() { + return 0; + } + + public void implement(String args, FileMapShellState state) + throws SomethingIsWrongException { + if (state.table == null) { + throw new SomethingIsWrongException("Table not found."); + } + ArrayList parameters = Parser.parseCommandArgs(args); + if (parameters.size() != 0) { + throw new SomethingIsWrongException("This command takes no parameters"); + } + MyTable temp = (MyTable) state.table; + List keySet = temp.list(); + if (keySet.size() == 0) { + System.out.println("\n"); + return; + } + + StringBuilder sb = new StringBuilder(""); + for (String key : keySet) { + sb.append(key); + sb.append(", "); + } + sb.deleteCharAt(sb.length() - 1); + sb.deleteCharAt(sb.length() - 1); + System.out.println(sb.toString()); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/MyTable.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/MyTable.java new file mode 100644 index 000000000..8af5d75fb --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/MyTable.java @@ -0,0 +1,19 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.util.List; + +import ru.fizteh.fivt.storage.strings.Table; + +public interface MyTable extends Table { + String getName(); + void setAutoCommit(boolean status); + boolean getAutoCommit(); + int getChangesCounter(); + List list(); + String get(String a); + String put(String key, String value); + String remove(String a); + int size(); + int commit(); + int rollback(); +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/PutCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/PutCommand.java new file mode 100644 index 000000000..e6104335b --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/PutCommand.java @@ -0,0 +1,40 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.util.ArrayList; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Parser; + +public class PutCommand> + extends SomeCommand { + + public String getCommandName() { + return "put"; + } + + public int getArgumentQuantity() { + return 2; + } + + public void implement(String args, State state) + throws SomethingIsWrongException { + if (state.getTable() == null) { + System.err.println("no table"); + return; + } + ArrayList parameters = Parser.parseCommandArgs(args); + if (parameters.size() != 2) { + throw new IllegalArgumentException("Use 2 arguments for this operation"); + } + Key key = state.parseKey(parameters.get(0)); + Value value = state.parseValue(parameters.get(1)); + Value temp = state.put(key, value); + if (temp != null) { + System.out.println("overwrite\n" + state.valueToString(temp)); + } else { + System.out.println("new"); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ReadingUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ReadingUtils.java new file mode 100644 index 000000000..6acadf993 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/ReadingUtils.java @@ -0,0 +1,141 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.*; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; + + +public class ReadingUtils { + + protected RandomAccessFile tempFile; + private static int valueShift = -1; + + public ReadingUtils(String pathToFile) throws SomethingIsWrongException { + try { + tempFile = new RandomAccessFile(pathToFile, "r"); + } catch (FileNotFoundException e) { + tempFile = null; + valueShift = 0; + return; + } + skipKey(); + valueShift = readOffset(); + try { + tempFile.seek(0); + } catch (IOException e) { + throw new SomethingIsWrongException("Error aqcuired while seeking through file: " + e.getMessage()); + } + } + + public String readKey() throws SomethingIsWrongException { + byte[] array; + try { + if (tempFile.getFilePointer() >= valueShift) { + return null; + } + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte b = tempFile.readByte(); + while (b != 0) { + bytes.write(b); + b = tempFile.readByte(); + } + array = UtilMethods.bytesToArray(bytes); + } catch (IOException e) { + throw new SomethingIsWrongException("Error acquired: " + e.getMessage()); + } + String returnKey; + try { + returnKey = new String(array, UtilMethods.ENCODING); + } catch (UnsupportedEncodingException e) { + throw new SomethingIsWrongException("Error acquired: " + e.getMessage()); + } + return returnKey; + } + + public String readValue() throws SomethingIsWrongException { + int offset = readOffset(); + int nextOffset = readNextOffset(); + long currentOffset; + try { + currentOffset = tempFile.getFilePointer(); + tempFile.seek(offset); + int valueLength = nextOffset - offset; + byte[] bytes = new byte[valueLength]; + tempFile.read(bytes, 0, valueLength); + tempFile.seek(currentOffset); + return new String(bytes, UtilMethods.ENCODING); + } catch (IOException e) { + throw new SomethingIsWrongException("Error acquired: " + e.getMessage()); + } + + } + + public boolean endOfFile() throws SomethingIsWrongException { + if (tempFile == null) { + return true; + } + + boolean result = true; + try { + result = (tempFile.getFilePointer() == valueShift); + } catch (EOFException ee) { + return result; + } catch (IOException e) { + if (e.getMessage() != null) { + throw new SomethingIsWrongException("Error aqcuired while reading a file " + e.getMessage()); + } + } + return result; + } + + + private int readNextOffset() throws SomethingIsWrongException { + int nextOffset = 0; + try { + int currentOffset = (int) tempFile.getFilePointer(); + if (readKey() == null) { + nextOffset = (int) tempFile.length(); + } else { + nextOffset = readOffset(); + } + tempFile.seek(currentOffset); + } catch (IOException e) { + if (e.getMessage() != null) { + if (e.getMessage() != null) { + throw new SomethingIsWrongException("Error aqcuired while reading a file " + e.getMessage()); + } + } + } + return nextOffset; + } + + private void skipKey() throws SomethingIsWrongException { + byte b = 0; + do { + try { + b = tempFile.readByte(); + } catch (IOException e) { + if (e.getMessage() != null) { + throw new SomethingIsWrongException("Error aqcuired while reading a file " + e.getMessage()); + } + } + } while(b != 0); + } + + private int readOffset() throws SomethingIsWrongException { + try { + return tempFile.readInt(); + } catch (IOException e) { + if (e.getMessage() != null) { + throw new SomethingIsWrongException("Error aqcuired while reading a file " + e.getMessage()); + } else { + throw new SomethingIsWrongException("Empty file"); + } + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/RemoveKeyCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/RemoveKeyCommand.java new file mode 100644 index 000000000..36522b46a --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/RemoveKeyCommand.java @@ -0,0 +1,38 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.util.ArrayList; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Parser; + +public class RemoveKeyCommand> + extends SomeCommand { + + public String getCommandName() { + return "remove"; + } + + public int getArgumentQuantity() { + return 1; + } + + public void implement(String args, State state) + throws SomethingIsWrongException { + if (state.getTable() == null) { + throw new SomethingIsWrongException("no table"); + } + ArrayList parameters = Parser.parseCommandArgs(args); + if (parameters.size() != 1) { + throw new IllegalArgumentException("Use 1 argument for this operation"); + } + Key key = state.parseKey(parameters.get(0)); + Value a = state.remove(key); + if (a == null) { + System.out.println("not found"); + } else { + System.out.println("removed"); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/RollbackCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/RollbackCommand.java new file mode 100644 index 000000000..4a244a42c --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/RollbackCommand.java @@ -0,0 +1,24 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + +public class RollbackCommand extends SomeCommand { + + public String getCommandName() { + return "rollback"; + } + + public int getArgumentQuantity() { + return 0; + } + + public void implement(String args, State state) + throws SomethingIsWrongException { + if (state.getTable() == null) { + throw new SomethingIsWrongException("no table"); + } + System.out.println(state.rollback()); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SimpleTableBuilder.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SimpleTableBuilder.java new file mode 100644 index 000000000..5ee2dc35a --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SimpleTableBuilder.java @@ -0,0 +1,33 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.io.File; +import java.util.Set; + +public class SimpleTableBuilder implements TableBuilder { + TableUsingStrings table; + + public String get(String key) { + return table.rawGet(key); + } + + public void put(String key, String value) { + table.rawPut(key, value); + } + + public Set getKeys() { + return table.unchangedOldData.keySet(); + } + + public File getTableDirectory() { + return new File(table.getParentDirectory(), table.getName()); + } + + public void setCurrentFile(File currentFile) { + } + + public SimpleTableBuilder(TableUsingStrings table) { + this.table = table; + } + + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SingleFileTable.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SingleFileTable.java new file mode 100644 index 000000000..961393f60 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SingleFileTable.java @@ -0,0 +1,39 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + + +import java.io.File; +import java.io.IOException; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + + +public class SingleFileTable extends SomeTable { + + public static final String DATABASENAME = "db.dat"; + + public SingleFileTable(String dir, String name) { + super(dir, name); + } + + protected void load() throws SomethingIsWrongException { + scanFromDisk(getPathToDatabase()); + } + + protected void save() throws SomethingIsWrongException { + writeOnDisk(unchangedOldData.keySet(), getPathToDatabase()); + } + + private String getPathToDatabase() { + File curDir = new File(new File(".").getAbsolutePath()); + File databaseFile; + try { + databaseFile = new File(curDir.getCanonicalPath(), DATABASENAME); + return databaseFile.getAbsolutePath(); + } catch (IOException e) { + e.getMessage(); + } + return ""; + } + + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SizeCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SizeCommand.java new file mode 100644 index 000000000..d4f55a716 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SizeCommand.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + +public class SizeCommand> + extends SomeCommand { + + public String getCommandName() { + return "size"; + } + + public int getArgumentQuantity() { + return 0; + } + + public void implement(String args, State state) + throws SomethingIsWrongException { + if (state.getTable() == null) { + throw new SomethingIsWrongException("no table"); + } + System.out.println(state.size()); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SomeStorage.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SomeStorage.java new file mode 100644 index 000000000..13c59995e --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SomeStorage.java @@ -0,0 +1,218 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + + +public abstract class SomeStorage { + protected HashMap unchangedOldData; + protected HashSet deletedKeys; + private String name; + protected String parentDirectory; + protected boolean doAutoCommit; + private final Lock transactionLock = new ReentrantLock(true); + + protected final ThreadLocal transaction = new ThreadLocal() { + public Transactions initialValue() { + return new Transactions(); + } + }; + + class Transactions { + HashMap currentData; + int size; + int unsavedChangesCounter; + + Transactions() { + this.currentData = new HashMap(); + this.size = 0; + this.unsavedChangesCounter = 0; + } + + public void newModification(Key key, Value value) { + currentData.put(key, value); + } + + public int saveModifications() { + int changesCounter = 0; + for (Key key : currentData.keySet()) { + Value newOne = currentData.get(key); + if (!GlobalUtils.compare(newOne, unchangedOldData.get(key))) { + if (newOne != null) { + unchangedOldData.put(key, newOne); + } else { + unchangedOldData.remove(key); + } + changesCounter++; + } + } + return changesCounter; + } + + public int calculateChangesQuantity() { + int changesCounter = 0; + for (Key key : currentData.keySet()) { + Value newOne = currentData.get(key); + if (!GlobalUtils.compare(newOne, unchangedOldData.get(key))) { + changesCounter++; + } + } + return changesCounter; + } + + public int calculateSize() { + int size = unchangedOldData.size(); + for (Key key : currentData.keySet()) { + Value newOne = currentData.get(key); + Value oldOne = unchangedOldData.get(key); + if (newOne == null && oldOne != null) { + size--; + } else if (newOne != null && oldOne == null) { + size++; + } + } + return size; + } + + public Value getVal(Key key) { + if (currentData.containsKey(key)) { + return currentData.get(key); + } + return unchangedOldData.get(key); + } + + public List getKeys() { + List result = new LinkedList(currentData.keySet()); + return result; + } + + public int getSize() { + return unchangedOldData.size() + calculateSize(); + } + + public void incrementUnsavedChangesCounter() { + unsavedChangesCounter++; + } + + public int getUnsavedChangesCounter() { + return unsavedChangesCounter; + } + + public void clear() { + currentData.clear(); + unsavedChangesCounter = 0; + size = 0; + } + + } + + public String getParentDirectory() { + return parentDirectory; + } + + public void setAutoCommit(boolean status) { + doAutoCommit = status; + } + + public boolean getAutoCommit() { + return doAutoCommit; + } + + public String getName() { + return name; + } + public int sizeOfStorage() { + return transaction.get().calculateSize(); + } + + public int getChangesCounter() { + return transaction.get().calculateChangesQuantity(); + } + + protected abstract void load() throws IOException; + protected abstract void save() throws IOException; + + public SomeStorage(String dir, String name) { + this.parentDirectory = dir; + this.name = name; + unchangedOldData = new HashMap(); + try { + load(); + } catch (IOException e) { + if (!e.getMessage().equals("didn't exist") + && !e.getMessage().equals("empty file")) { + throw new IllegalArgumentException( + "invalid file format " + e.getMessage()); + } + } + + } + + public Value getFromStorage(Key key) { + if (key == null) { + throw new IllegalArgumentException("key cannot be null"); + } + return transaction.get().getVal(key); + } + + public List getAllKeys() { + return transaction.get().getKeys(); + } + + public Value putIntoStorage(Key key, Value value) { + Value oldVal = transaction.get().getVal(key); + transaction.get().newModification(key, value); + return oldVal; + } + + public Value removeFromStorage(Key key) { + if (key == null) { + throw new IllegalArgumentException("key cannot be null"); + } + if (getFromStorage(key) == null) { + return null; + } + Value oldVal = transaction.get().getVal(key); + transaction.get().newModification(key, null); + transaction.get().incrementUnsavedChangesCounter(); + return oldVal; + } + + public int rollbackStorage() { + int deletedOrAdded = transaction.get().calculateChangesQuantity(); + transaction.get().clear(); + return deletedOrAdded; + } + + public int commitStorage() { + try { + transactionLock.lock(); + int commitCount = transaction.get().saveModifications(); + transaction.get().clear(); + try { + save(); + } catch (IOException e) { + System.err.println("commit error: " + e.getMessage()); + return 0; + } + return commitCount; + } finally { + transactionLock.unlock(); + } + } + + void rawPut(Key key, Value value) { + unchangedOldData.put(key, value); + } + + Value rawGet(Key key) { + return unchangedOldData.get(key); + } + + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SomeTable.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SomeTable.java new file mode 100644 index 000000000..cdaae90df --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/SomeTable.java @@ -0,0 +1,183 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.*; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + + +public abstract class SomeTable implements MyTable { + protected static HashMap unchangedOldData; + protected static HashMap currentData; + protected static HashSet deletedKeys; + private String name; + private String parentDirectory; + private static int size; + public static int unsavedChangesCounter; + protected boolean autoCommit = false; //to block JUnit functional for a filemap + protected abstract void load() throws SomethingIsWrongException; + protected abstract void save() throws SomethingIsWrongException; + + + public void setAutoCommit(boolean status) { + autoCommit = status; + } + + public boolean getAutoCommit() { + return autoCommit; + } + + public List list() { + List toReturnSafe = new LinkedList(currentData.keySet()); + return toReturnSafe; + } + + public int getChangesCounter() { + return unsavedChangesCounter; + } + + public String getParentDirectory() { + return parentDirectory; + } + + public String getName() { + return name; + } + public int size() { + return size; + } + + public SomeTable(String dir, String name) { + this.parentDirectory = dir; + this.name = name; + unchangedOldData = new HashMap(); + currentData = new HashMap(); + deletedKeys = new HashSet(); + unsavedChangesCounter = 0; + try { + load(); + } catch (SomethingIsWrongException e) { + if (!e.getMessage().equals("Unable to scan from disc.") && !e.getMessage().equals("Empty file")) { + System.err.println("Error aqcuired while opening a table. Message: " + e.getMessage()); + } + } + + } + + public String get(String key) { + if (currentData.containsKey(key)) { + return currentData.get(key); + } else if (deletedKeys.contains(key)) { + return null; + } + return unchangedOldData.get(key); + } + + public String put(String key, String newValue) { + String value = oldValue(key); + currentData.put(key, newValue); + if (value == null) { + ++size; + } + ++unsavedChangesCounter; + return value; + } + + public String remove(String key) { + String value = oldValue(key); + if (currentData.containsKey(key)) { + currentData.remove(key); + if (unchangedOldData.containsKey(key)) { + deletedKeys.add(key); + } + } else { + deletedKeys.add(key); + } + if (value != null) { + --size; + } + ++unsavedChangesCounter; + return value; + } + + private static String oldValue(String key) { + String oldValue = currentData.get(key); + if (oldValue == null && !deletedKeys.contains(key)) { + oldValue = unchangedOldData.get(key); + } + return oldValue; + } + + private int keySize(Set keys) { + int keysSize = 0; + for (String key : keys) { + keysSize += UtilMethods.countBytes(key, UtilMethods.ENCODING) + 5; + } + return keysSize; + } + + public void writeOnDisk(Set keys, String file) throws SomethingIsWrongException { + WritingUtils write = new WritingUtils(file); + int temp = keySize(keys); + for (String key : keys) { + write.writeKey(key); + write.writeOffset(temp); + temp += UtilMethods.countBytes(unchangedOldData.get(key), UtilMethods.ENCODING); + } + for (String key : keys) { + String tempCheck = unchangedOldData.get(key); + if (tempCheck != null) { + write.writeValue(tempCheck); + } + } + UtilMethods.closeCalm(write.dataFile); + } + + public void scanFromDisk(String file) throws SomethingIsWrongException { + if (!UtilMethods.doesExist(file)) { + throw new SomethingIsWrongException("Unable to scan from disc."); + } + ReadingUtils read = new ReadingUtils(file); + while (!read.endOfFile()) { + String key = read.readKey(); + String value = read.readValue(); + unchangedOldData.put(key, value); + } + UtilMethods.closeCalm(read.tempFile); + } + + public int rollback() { + int deletedOrAdded = Math.abs(unchangedOldData.size() - size); + deletedKeys.clear(); + currentData.clear(); + size = unchangedOldData.size(); + unsavedChangesCounter = 0; + return deletedOrAdded; + } + + public int commit() { + int commitCount = Math.abs(unchangedOldData.size() - size); + for (final String toDelete : deletedKeys) { + unchangedOldData.remove(toDelete); + } + for (String toAdd : currentData.keySet()) { + unchangedOldData.put(toAdd, currentData.get(toAdd)); + } + size = unchangedOldData.size(); + try { + save(); + } catch (SomethingIsWrongException e) { + System.out.println("Error aqcuired while commiting changes. Message: " + e.getMessage()); + } + unsavedChangesCounter = 0; + deletedKeys.clear(); + currentData.clear(); + return commitCount; + } + + + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/Table.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/Table.java new file mode 100644 index 000000000..240eaf373 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/Table.java @@ -0,0 +1,12 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + + +public interface Table { + String getName(); + String get(String a); + String put(String a, String b); + String remove(String a); + int size(); + int commit(); + int rollback(); +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/TableBuilder.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/TableBuilder.java new file mode 100644 index 000000000..d2aab8f10 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/TableBuilder.java @@ -0,0 +1,16 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.io.File; +import java.util.Set; + +public interface TableBuilder { + String get(String key); + + void put(String key, String value); + + Set getKeys(); + + File getTableDirectory(); + + void setCurrentFile(File currentFile); +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/TableUsingStrings.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/TableUsingStrings.java new file mode 100644 index 000000000..969fab330 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/TableUsingStrings.java @@ -0,0 +1,39 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + +import java.util.List; + + +public abstract class TableUsingStrings extends SomeStorage implements MyTable { + protected TableUsingStrings(String dir, String name) { + super(dir, name); + } + + public String remove(String key) { + return removeFromStorage(key); + } + + public int size() { + return sizeOfStorage(); + } + + public String get(String key) { + return getFromStorage(key); + } + + public String put(String key, String value) { + return putIntoStorage(key, value); + } + + public int rollback() { + return rollbackStorage(); + } + + public int commit() { + return commitStorage(); + } + + public List list() { + return getAllKeys(); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/WritingUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/WritingUtils.java new file mode 100644 index 000000000..64fbb5e57 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/filemap/WritingUtils.java @@ -0,0 +1,58 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap; + + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.*; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.UnsupportedEncodingException; + + + +public class WritingUtils { + + protected RandomAccessFile dataFile; + public WritingUtils(String filePath) throws SomethingIsWrongException { + try { + dataFile = new RandomAccessFile(filePath, "rw"); + } catch (FileNotFoundException e) { + throw new SomethingIsWrongException(String.format("error while creating file: '%s'", filePath)); + } + try { + dataFile.setLength(0); + } catch (IOException e) { + throw new SomethingIsWrongException("Error aqcuired while resizing a file " + e.getMessage()); + } + } + + public void writeKey(String key) throws SomethingIsWrongException { + byte[] bytes = UtilMethods.getBytes(key, UtilMethods.ENCODING); + try { + dataFile.write(bytes); + dataFile.writeByte(0); + } catch (IOException e) { + throw new SomethingIsWrongException("Error acquired while writing to file: " + e.getMessage()); + } + } + + public void writeOffset(int offset) throws SomethingIsWrongException { + try { + dataFile.writeInt(offset); + } catch (IOException e) { + throw new SomethingIsWrongException("Error acquired while writing to file: " + e.getMessage()); + } + } + + public void writeValue(String value) throws SomethingIsWrongException { + try { + byte[] bytes = value.getBytes(UtilMethods.ENCODING); + dataFile.write(bytes); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException("Unrecognized encoding"); + } catch (IOException e) { + throw new SomethingIsWrongException("Error acquired while writing to file: " + e.getMessage()); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/CreateCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/CreateCommand.java new file mode 100644 index 000000000..8edc64b55 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/CreateCommand.java @@ -0,0 +1,33 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GlobalUtils; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + +public class CreateCommand> + extends SomeCommand { + public String getCommandName() { + return "create"; + } + + public int getArgumentQuantity() { + return 1; + } + + public void implement(String args, State state) throws SomethingIsWrongException { + Table newOne = null; + String tableName = GlobalUtils.parseTableName(args); + try { + newOne = state.createTable(args); + if (newOne != null) { + System.out.println("created"); + } else { + System.out.println(tableName + " exists"); + } + } catch (IllegalArgumentException e) { + throw new SomethingIsWrongException(e.getMessage()); + } + + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/Database.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/Database.java new file mode 100644 index 000000000..75badc24e --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/Database.java @@ -0,0 +1,101 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.io.File; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GlobalUtils; + +public class Database implements TableProvider { + private static final String CHECK_NAME_EXPRESSION = "[^0-9A-Za-zА-Яа-я]+"; + HashMap content = new HashMap(); + private String databaseDirectoryPath; + private MultifileTable currentTable = null; + + public Database(String databaseDirectoryPath) { + this.databaseDirectoryPath = databaseDirectoryPath; + File databaseDirectory = new File(databaseDirectoryPath); + for (File tableFile : databaseDirectory.listFiles()) { + if (tableFile.isFile()) { + continue; + } + MultifileTable table = new MultifileTable(databaseDirectoryPath, tableFile.getName()); + content.put(table.getName(), table); + } + } + + public MultifileTable getTable(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("table's name cannot be null"); + } + checkName(name); + MultifileTable table = content.get(name); + if (table == null) { + return table; + } + if (currentTable != null) { + int changes = currentTable.getChangesCounter(); + if (changes > 0 && !currentTable.getAutoCommit()) { + throw new IllegalStateException(changes + " unsaved changes"); + } else if (currentTable.getAutoCommit()) { + currentTable.commit(); + } + } + + currentTable = table; + return table; + } + + public HashMap showTables() { + HashMap tables = new HashMap(); + for (Entry contents : content.entrySet()) { + tables.put(contents.getKey(), contents.getValue().size()); + } + return tables; + } + + public MultifileTable createTable(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Table's name cannot be null"); + } + checkName(name); + if (content.containsKey(name)) { + return null; + } + File tableDirectory = new File(databaseDirectoryPath, name); + if (!tableDirectory.exists()) { + tableDirectory.mkdir(); + } + MultifileTable table = new MultifileTable(databaseDirectoryPath, name); + content.put(name, table); + return table; + } + + public void removeTable(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("Table's name cannot be null"); + } + if (!content.containsKey(name)) { + throw new IllegalStateException(name + " not exists"); + } + if (currentTable != null) { + if (currentTable.getName().equals(name)) { + currentTable = null; + } + } + content.remove(name); + File tableFile = new File(databaseDirectoryPath, name); + GlobalUtils.deleteFile(tableFile); + } + + private void checkName(String name) { + Pattern pattern = Pattern.compile(CHECK_NAME_EXPRESSION); + Matcher matcher = pattern.matcher(name); + if (matcher.find()) { + throw new IllegalArgumentException("bad symbol in table's name"); + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DatabaseFactory.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DatabaseFactory.java new file mode 100644 index 000000000..127f2a0c1 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DatabaseFactory.java @@ -0,0 +1,24 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + + +import java.io.File; + +import ru.fizteh.fivt.storage.strings.TableProvider; + + + +public class DatabaseFactory implements MultiFileMapTableProviderClassFactory { + public TableProvider create(String directory) { + if (directory == null || directory.isEmpty()) { + throw new IllegalArgumentException("directory name cannot be null"); + } + File databaseDirectory = new File(directory); + if (databaseDirectory.isFile()) { + throw new IllegalArgumentException("it must be directory, not file"); + } + if (!databaseDirectory.exists()) { + databaseDirectory.mkdir(); + } + return new Database(databaseDirectory.getAbsolutePath()); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DatabaseTest.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DatabaseTest.java new file mode 100644 index 000000000..63932ea70 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DatabaseTest.java @@ -0,0 +1,92 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + + +import java.io.File; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import ru.fizteh.fivt.storage.strings.*; + +public class DatabaseTest { + + TableProviderFactory factory; + TableProvider provider; + + @Before + public void beforeTest() { + factory = new DatabaseFactory(); + provider = factory.create("C:\\temp\\database_test"); + provider.createTable("table1"); + provider.createTable("table2"); + } + + @Test + public void testCreateTable() throws Exception { + // non-existing tables + Assert.assertNotNull(provider.createTable("newtable1")); + Assert.assertNotNull(provider.createTable("newtable2")); + // existing tables + Assert.assertNull(provider.createTable("table1")); + Assert.assertNull(provider.createTable("table2")); + + // clean-up + provider.removeTable("newtable1"); + provider.removeTable("newtable2"); + } + + @Test + public void testGetTable() throws Exception { + // non-existing tables + Assert.assertNull(provider.getTable("nonexistingtable")); + Assert.assertNull(provider.getTable("thereisnosuchtable")); + // existing tables + Assert.assertNotNull(provider.getTable("table1")); + Assert.assertNotNull(provider.getTable("table2")); + + Table table1 = provider.getTable("table1"); + Assert.assertEquals(table1, provider.getTable("table1")); + } + + @Test(expected = IllegalArgumentException.class) + public void testGetTableExceptions() { + provider.getTable(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateTableExceptions() { + provider.createTable(null); + } + + @Test + public void testRemoveTable() throws Exception { + //prepare + provider.createTable("newtable1"); + provider.createTable("newtable2"); + + // existing tables + provider.removeTable("newtable1"); + provider.removeTable("newtable2"); + } + + @Test(expected = IllegalArgumentException.class) + public void testRemoveTableIllegalArgumentException() { + provider.removeTable(null); + } + + @Test(expected = IllegalStateException.class) + public void testRemoveTableIllegalStateException() { + provider.removeTable("nonexistingtable"); + provider.removeTable("nosuchtable"); + } + + @After + public void testAfter() { + provider.removeTable("table1"); + provider.removeTable("table2"); + File f = new File("C:\\temp\\database_test"); + f.delete(); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DropCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DropCommand.java new file mode 100644 index 000000000..f0b589954 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/DropCommand.java @@ -0,0 +1,34 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; +import java.io.IOException; +import java.util.ArrayList; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Parser; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + + +public class DropCommand extends SomeCommand { + public String getCommandName() { + return "drop"; + } + + public int getArgumentQuantity() { + return 1; + } + + public void implement(String args, State state) throws SomethingIsWrongException { + ArrayList parameters = Parser.parseCommandArgs(args); + if (parameters.size() != 1) { + throw new IllegalArgumentException("Correct number of arguments -- 1"); + } + try { + state.dropTable(parameters.get(0)); + System.out.println("dropped"); + } catch (IOException e) { + throw new SomethingIsWrongException(e.getMessage()); + } catch (IllegalStateException e) { + throw new SomethingIsWrongException(e.getMessage()); + } + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapReadingUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapReadingUtils.java new file mode 100644 index 000000000..6189f5a5a --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapReadingUtils.java @@ -0,0 +1,34 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.io.File; +import java.io.IOException; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.FileMapReadingUtils; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.TableBuilder; + + +public class MultiFileMapReadingUtils { + public static void load(TableBuilder build) throws IOException { + File tableDir = build.getTableDirectory(); + if (tableDir.listFiles() == null) { + return; + } + + for (File dir : tableDir.listFiles()) { + if (dir.isFile()) { + continue; + } + + if (dir.listFiles().length == 0) { + throw new IllegalArgumentException("empty bucket"); + } + + for (File file : dir.listFiles()) { + build.setCurrentFile(file); + FileMapReadingUtils.scanFromDisk(file.getAbsolutePath(), build); + } + } + + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapShellState.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapShellState.java new file mode 100644 index 000000000..ecf0934a4 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapShellState.java @@ -0,0 +1,91 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + + +import java.io.IOException; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.FileMapShellState; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.MyTable; + +public class MultiFileMapShellState extends FileMapShellState + implements MultifileMapShellStateInterface { + public MultiFileMapTableProviderClass tableProvider; + + public MyTable useTable(String name) { + MyTable tempTable = (MyTable) tableProvider.getTable(name); + if (tempTable != null) { + table = tempTable; + } + return tempTable; + } + + public MyTable createTable(String args) { + return (MyTable) tableProvider.createTable(args); + } + + public void dropTable(String name) throws IOException { + tableProvider.removeTable(name); + if (table.getName().equals(name)) { + table = null; + } + } + + public String getCurrentTableName() { + return table.getName(); + } + + @Override + public String put(String key, String value) { + return table.put(key, value); + } + + @Override + public String get(String key) { + return table.get(key); + } + + @Override + public int commit() { + return table.commit(); + } + + @Override + public int rollback() { + return table.rollback(); + } + + @Override + public int size() { + return table.size(); + } + + @Override + public String remove(String key) { + return table.remove(key); + } + + @Override + public MyTable getTable() { + return (MyTable) table; + } + + @Override + public String keyToString(String key) { + return key; + } + + @Override + public String valueToString(String value) { + return value; + } + + @Override + public String parseKey(String key) { + return key; + } + + @Override + public String parseValue(String value) { + return value; + } +} + diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableBuilder.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableBuilder.java new file mode 100644 index 000000000..92855f74d --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableBuilder.java @@ -0,0 +1,27 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.io.File; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GlobalUtils; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.SimpleTableBuilder; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.TableUsingStrings; + +public class MultiFileMapTableBuilder extends SimpleTableBuilder { + private int currentDir; + private int currentFile; + + + public MultiFileMapTableBuilder(TableUsingStrings table) { + super(table); + } + + public void setCurrentFile(File file) { + currentDir = GlobalUtils.parseDirNumber(file.getParentFile()); + currentFile = GlobalUtils.parseFileNumber(file); + } + + public void put(String key, String value) { + GlobalUtils.checkKeyPlacement(key, currentDir, currentFile); + super.put(key, value); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableProviderClass.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableProviderClass.java new file mode 100644 index 000000000..5ff9f50b7 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableProviderClass.java @@ -0,0 +1,9 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.util.HashMap; + +import ru.fizteh.fivt.storage.strings.TableProvider; + +public interface MultiFileMapTableProviderClass extends TableProvider { + HashMap showTables(); +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableProviderClassFactory.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableProviderClassFactory.java new file mode 100644 index 000000000..19ab5b4cd --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapTableProviderClassFactory.java @@ -0,0 +1,7 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import ru.fizteh.fivt.storage.strings.TableProviderFactory; + +public interface MultiFileMapTableProviderClassFactory extends TableProviderFactory { + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapWritingUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapWritingUtils.java new file mode 100644 index 000000000..893d78491 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultiFileMapWritingUtils.java @@ -0,0 +1,57 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.FileMapWritingUtils; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GlobalUtils; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.TableBuilder; + +public class MultiFileMapWritingUtils { + + public static void save(TableBuilder build) throws IOException { + File tableDir = build.getTableDirectory(); + if (tableDir.listFiles() == null) { + return; + } + ArrayList> toSave = new ArrayList>(); + boolean dirIsEmpty; + + for (int dirNumber = 0; dirNumber < GlobalUtils.DIR_QUANTITY; ++dirNumber) { + toSave.clear(); + for (int fileNumber = 0; fileNumber < GlobalUtils.FILES_PER_DIR; ++fileNumber) { + toSave.add(new HashSet()); + } + dirIsEmpty = true; + + for (String key : build.getKeys()) { + if (GlobalUtils.getDirNumber(key) == dirNumber) { + int fileNumber = GlobalUtils.getFileNumber(key); + toSave.get(fileNumber).add(key); + dirIsEmpty = false; + } + } + String dirName = dirNumber + ".dir"; + File dir = new File(tableDir, dirName); + if (dirIsEmpty) { + GlobalUtils.deleteFile(dir); + } + for (int fileNumber = 0; fileNumber < GlobalUtils.FILES_PER_DIR; ++fileNumber) { + String fileName = fileNumber + ".dat"; + File file = new File(dir, fileName); + if (toSave.get(fileNumber).isEmpty()) { + GlobalUtils.deleteFile(file); + continue; + } + if (!dir.exists()) { + dir.mkdir(); + } + FileMapWritingUtils.writeOnDisk(toSave.get(fileNumber), + file.getAbsolutePath(), build); + } + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileMapMain.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileMapMain.java new file mode 100644 index 000000000..57606eba7 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileMapMain.java @@ -0,0 +1,50 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.util.ArrayList; +import java.util.HashSet; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.CommitCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.ExitCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.FileMapShellState; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GetCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.ListCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.PutCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.RemoveKeyCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.RollbackCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.SizeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Commands; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Shell; + + +public class MultifileMapMain { + public static void main(String[] args) { + HashSet> com = new HashSet>() { { + add(new ExitCommand()); add(new RollbackCommand()); + add(new CommitCommand()); add(new ListCommand()); + add(new PutCommand()); add(new GetCommand()); + add(new RemoveKeyCommand()); add(new SizeCommand()); }}; + HashSet> com1 = new HashSet>() + { { add(new DropCommand()); add(new UseCommand()); + add(new CreateCommand()); }}; + ArrayList> res = new ArrayList>(); + res.addAll(com); + res.addAll(com1); + HashSet> actualResult = new HashSet>(res); + Shell shell = new Shell(actualResult); + try { + String dbDirectory = System.getProperty("fizteh.db.dir"); + if (dbDirectory == null) { + System.err.println("error: nope. Gimme something."); + System.exit(-2); + } + MultiFileMapShellState state = new MultiFileMapShellState(); + DatabaseFactory factory = new DatabaseFactory(); + state.tableProvider = (MultiFileMapTableProviderClass) factory.create(dbDirectory); + shell.setShellState(state); + } catch (IllegalArgumentException e) { + System.err.println("error: " + e.getMessage()); + System.exit(-1); + } + shell.run(args, shell); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileMapShellStateInterface.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileMapShellStateInterface.java new file mode 100644 index 000000000..ee15ba0bc --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileMapShellStateInterface.java @@ -0,0 +1,16 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.io.IOException; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.FileMapShellStateInterface; + +public interface MultifileMapShellStateInterface + extends FileMapShellStateInterface { + Table useTable(String name); + + Table createTable(String arguments); + + void dropTable(String name) throws IOException; + + String getCurrentTableName(); +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileTable.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileTable.java new file mode 100644 index 000000000..738f3fdd5 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileTable.java @@ -0,0 +1,35 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.io.IOException; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.SimpleTableBuilder; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.TableUsingStrings; + + +public class MultifileTable extends TableUsingStrings { + + + + public MultifileTable(String directory, String tableName) { + super(directory, tableName); + } + + /*private File getTableDirectory() { + File tableDirectory = new File(getParentDirectory(), getName()); + if (!tableDirectory.exists()) { + tableDirectory.mkdir(); + } + return tableDirectory; + }*/ + + protected void load() throws IOException { + MultiFileMapReadingUtils.load(new SimpleTableBuilder(this)); + + } + + protected void save() throws IOException { + MultiFileMapWritingUtils.save(new SimpleTableBuilder(this)); + + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileTableTest.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileTableTest.java new file mode 100644 index 000000000..58c719e65 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/MultifileTableTest.java @@ -0,0 +1,172 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.util.Random; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import ru.fizteh.fivt.storage.strings.TableProvider; +import ru.fizteh.fivt.storage.strings.TableProviderFactory; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.MyTable; + +public class MultifileTableTest { + + private static final int KEYS_COUNT = 20; + private static final String TABLE_NAME = "testtable"; + MyTable currentTable; + + TableProviderFactory factory = new DatabaseFactory(); + TableProvider provider = factory.create("C:\\temp\\database_test"); + + + @Before + public void gettingReady() throws Exception { + currentTable = (MyTable) provider.createTable(TABLE_NAME); + for (int index = 0; index < KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + String value = String.format("value%d", index); + currentTable.put(key, value); + } + } + + @Test + public void testForNewData() { + // new data + for (int index = 0; index < KEYS_COUNT; ++index) { + String newKey = String.format("new_key%d", index); + String newValue = String.format("new_value%d", index); + Assert.assertNull(currentTable.put(newKey, newValue)); + } + } + + @Test + public void testForExistingData() { + // existing data + for (int index = 0; index < KEYS_COUNT; ++index) { + String expectedValue = String.format("value%d", index); + String key = String.format("key%d", index); + Assert.assertEquals(expectedValue, currentTable.get(key)); + } + } + + @Test + public void testForUnexistingData() { + // non-existing data + Random random = new Random(); + for (int index = 0; index < KEYS_COUNT; ++index) { + String key = String.format("k%d", random.nextInt(100)); + Assert.assertNull(currentTable.get(key)); + } + } + + @Test + public void testForReplaceData() { + // replacing + for (int index = 0; index < KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + String oldValue = String.format("value%d", index); + String newValue = String.format("new_value%d", index); + Assert.assertEquals(oldValue, currentTable.put(key, newValue)); + } + } + + @Test + public void testCommit() { + int committed = currentTable.commit(); + Assert.assertEquals(KEYS_COUNT, committed); + + for (int index = 0; index < 2 * KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + String value = String.format("value%d", index); + currentTable.put(key, value); + } + + Assert.assertEquals(KEYS_COUNT, currentTable.commit()); + + for (int index = 0; index < 2 * KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + Assert.assertNotNull(currentTable.get(key)); + } + } + + @Test + public void testRollback() { + Assert.assertEquals(KEYS_COUNT, currentTable.rollback()); + + for (int index = 0; index < 2 * KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + String value = String.format("value%d", index); + currentTable.put(key, value); + } + + Assert.assertEquals(2 * KEYS_COUNT, currentTable.rollback()); + + for (int index = 0; index < 2 * KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + Assert.assertNull(currentTable.get(key)); + } + } + + @Test + public void testSize() { + Assert.assertEquals(KEYS_COUNT, currentTable.size()); + } + + @Test + public void testGetName() { + Assert.assertEquals(TABLE_NAME, currentTable.getName()); + } + + @Test(expected = IllegalArgumentException.class) + public void testTableExceptions() { + // get + currentTable.get(null); + + // storagePut + currentTable.put(null, "value"); + currentTable.put("key", null); + + // storageRemove + currentTable.remove(null); + } + + @Test + public void testRollbackAndCommit() { + for (int index = 0; index < KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + String value = String.format("value%d", index); + currentTable.put(key, value); + } + currentTable.commit(); + for (int index = 0; index < KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + currentTable.remove(key); + } + for (int index = 0; index < KEYS_COUNT; ++index) { + String key = String.format("key%d", index); + String value = String.format("value%d", index); + currentTable.put(key, value); + } + Assert.assertEquals(0, currentTable.rollback()); + + currentTable.remove("non-exists"); + currentTable.remove("non-exists1"); + currentTable.remove("key1"); + currentTable.put("key1", "value1"); + Assert.assertEquals(0, currentTable.rollback()); + + currentTable.put("key1", "value1"); + currentTable.commit(); + currentTable.remove("key1"); + currentTable.put("key1", "value1"); + Assert.assertEquals(0, currentTable.rollback()); + } + + @After + public void cleaningUp() throws Exception { + provider.removeTable(TABLE_NAME); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/ShowTablesCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/ShowTablesCommand.java new file mode 100644 index 000000000..974322b48 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/ShowTablesCommand.java @@ -0,0 +1,28 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map.Entry; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.*; + +public class ShowTablesCommand implements Commands { + public String getCommandName() { + return "show"; + } + + public int getArgumentQuantity() { + return 1; + } + + public void implement(String args, MultiFileMapShellState state) throws SomethingIsWrongException { + ArrayList parameters = Parser.parseCommandArgs(args); + if (!parameters.get(0).equals("tables")) { + throw new SomethingIsWrongException("no command with this name"); + } + HashMap tables = state.tableProvider.showTables(); + for (Entry iterator : tables.entrySet()) { + System.out.println(iterator.getKey() + ' ' + iterator.getValue()); + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/UseCommand.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/UseCommand.java new file mode 100644 index 000000000..76c96415c --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/multifilemap/UseCommand.java @@ -0,0 +1,44 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap; + + +import java.util.ArrayList; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Parser; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomeCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.SomethingIsWrongException; + +public class UseCommand> + extends SomeCommand { + public String getCommandName() { + return "use"; + } + + public int getArgumentQuantity() { + return 1; + } + + public void implement(String args, State state) throws SomethingIsWrongException { + ArrayList parameters = Parser.parseCommandArgs(args); + if (parameters.size() != 1) { + throw new IllegalArgumentException("Correct number of arguments -- 1"); + } + Table newOne = null; + try { + newOne = state.useTable(parameters.get(0)); + } catch (IllegalStateException e) { + System.err.println(e.getMessage()); + return; + } catch (IllegalArgumentException e) { + System.err.println(e.getMessage()); + return; + } + + if (newOne == null) { + System.out.println(parameters.get(0) + " not exists"); + return; + } + + System.out.println("using " + state.getCurrentTableName()); + + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseRow.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseRow.java new file mode 100644 index 000000000..ab4fbe62f --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseRow.java @@ -0,0 +1,129 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; + +public class DatabaseRow implements Storeable { + final List> classes = new ArrayList<>(); + List columns = new ArrayList<>(); + + public DatabaseRow(List> classes) { + this.classes.addAll(classes); + for (int i = 0; i < classes.size(); ++i) { + columns.add(null); + } + } + + public DatabaseRow() {} + + private void checkBounds(int index) throws IndexOutOfBoundsException { + if (index < 0 || index >= classes.size()) { + throw new IndexOutOfBoundsException(String.format("index out of bound: %d", index)); + } + } + + private void checkColumnType(int columnIndex, Class actualType) throws ColumnFormatException { + if (!actualType.isAssignableFrom(classes.get(columnIndex))) { + throw new ColumnFormatException(String.format("incorrect type: expected type: %s actual type: %s", + classes.get(columnIndex).getName(), actualType.getName())); + } + } + + public boolean equals(Object obj) { + DatabaseRow otherStoreable = (DatabaseRow) obj; + return otherStoreable.columns.equals(columns) && otherStoreable.classes.equals(classes); + } + + public int hashCode() { + return classes.hashCode() + columns.hashCode() * 17; + } + + public String getStringAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkColumnType(columnIndex, String.class); + return (String) columns.get(columnIndex); + } + + public Object getColumnAt(int columnIndex) throws IndexOutOfBoundsException { + checkBounds(columnIndex); + return columns.get(columnIndex); + } + + public Integer getIntAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkColumnType(columnIndex, Integer.class); + return (Integer) columns.get(columnIndex); + } + + public Long getLongAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkColumnType(columnIndex, Long.class); + return (Long) columns.get(columnIndex); + } + + public Byte getByteAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkColumnType(columnIndex, Byte.class); + return (Byte) columns.get(columnIndex); + } + + public Float getFloatAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkColumnType(columnIndex, Float.class); + return (Float) columns.get(columnIndex); + } + + public Double getDoubleAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkColumnType(columnIndex, Double.class); + return (Double) columns.get(columnIndex); + } + + public Boolean getBooleanAt(int columnIndex) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + checkColumnType(columnIndex, Boolean.class); + return (Boolean) columns.get(columnIndex); + } + + @Override + public String toString() { + return LocalUtils.join(columns); + } + + public void setColumns(List values) throws ColumnFormatException, IndexOutOfBoundsException { + if (values.size() != classes.size()) { + throw new IndexOutOfBoundsException(); + } + + columns.clear(); + + for (int index = 0; index < values.size(); ++index) { + columns.add(values.get(index)); + } + } + + public void setColumnAt(int columnIndex, Object value) throws ColumnFormatException, IndexOutOfBoundsException { + checkBounds(columnIndex); + if (value != null) { + checkColumnType(columnIndex, value.getClass()); + try { + LocalUtils.checkValue(value, value.getClass()); + } catch (ParseException e) { + throw new IllegalArgumentException("incorrect value: " + value.toString()); + } + } + columns.set(columnIndex, value); + } + + public void addColumn(Class columnType) { + classes.add(columnType); + columns.add(null); + } + + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseRowTest.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseRowTest.java new file mode 100644 index 000000000..da25dfb92 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseRowTest.java @@ -0,0 +1,63 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; + +import java.util.ArrayList; +import java.util.List; + +public class DatabaseRowTest { + Storeable storeable; + + @Before + public void setUp() { + List> columnTypes = new ArrayList<>(); + columnTypes.add(Integer.class); + columnTypes.add(String.class); + storeable = new DatabaseRow(columnTypes); + } + + @After + public void tearDown() throws Exception { + storeable = null; + } + + + @Test(expected = IndexOutOfBoundsException.class) + public void putValueOutOfBound() { + storeable.setColumnAt(3, null); + } + + @Test + public void putNullValueShouldPass() { + storeable.setColumnAt(0, null); + storeable.setColumnAt(1, null); + } + + @Test(expected = ColumnFormatException.class) + public void putIncorrectType1ShouldFail() { + storeable.setColumnAt(0, "adasda"); + } + + @Test(expected = ColumnFormatException.class) + public void putIncorrectType2ShouldFail() { + storeable.setColumnAt(1, 2); + } + + @Test(expected = ColumnFormatException.class) + public void putIncorrectType3ShouldFail() { + storeable.setColumnAt(1, new SimpleClass()); + } + + @Test + public void putCorrectValueShouldPass() throws Exception { + storeable.setColumnAt(1, "String"); + } +} + +class SimpleClass { +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTable.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTable.java new file mode 100644 index 000000000..e53f9eae3 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTable.java @@ -0,0 +1,170 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.text.ParseException; +import java.util.List; +import java.util.Set; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.SomeStorage; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap.MultiFileMapReadingUtils; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap.MultiFileMapWritingUtils; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; + +public class DatabaseTable extends SomeStorage implements Table { + + DatabaseTableProvider provider; + private List> columnTypes; + + public DatabaseTable(DatabaseTableProvider provider, String databaseDir, + String name, List> columnTypes) { + super(databaseDir, name); + if (columnTypes == null || columnTypes.isEmpty()) { + throw new IllegalArgumentException("column types cannot be null"); + } + this.columnTypes = columnTypes; + this.provider = provider; + try { + checkTableDirectory(); + load(); + } catch (IOException e) { + throw new IllegalArgumentException("invalid file format"); + } + } + + public int getNumberOfUncommittedChanges() { + return getChangesCounter(); + } + + public List list() { + return getAllKeys(); + } + + public Storeable get(String key) { + return getFromStorage(key); + } + + public Storeable put(String key, Storeable value) throws ColumnFormatException { + if (key != null) { + if (LocalUtils.checkStringCorrect(key)) { + throw new IllegalArgumentException("key cannot be empty"); + } + } else if (key == null) { + throw new IllegalArgumentException("key can't be null"); + } + + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + + if (!checkAlienStoreable(value)) { + throw new ColumnFormatException("alien storeable"); + } + isStoreableCorrect(value); + return putIntoStorage(key, value); + } + + public Storeable remove(String key) { + return removeFromStorage(key); + } + + public int size() { + return sizeOfStorage(); + } + + public int commit() throws IOException { + return commitStorage(); + } + + public int rollback() { + return rollbackStorage(); + } + + public int getColumnsCount() { + return columnTypes.size(); + } + + public Class getColumnType(int columnIndex) throws IndexOutOfBoundsException { + if (columnIndex < 0 || columnIndex > getColumnsCount()) { + throw new IndexOutOfBoundsException(); + } + return columnTypes.get(columnIndex); + } + + protected void load() throws IOException { + if (provider == null) { + return; + } + MultiFileMapReadingUtils.load(new StoreableTableBuilder(provider, this)); + } + + protected void save() throws IOException { + MultiFileMapWritingUtils.save(new StoreableTableBuilder(provider, this)); + } + + private void checkTableDirectory() throws IOException { + File tableDirectory = new File(getParentDirectory(), getName()); + if (!tableDirectory.exists()) { + tableDirectory.mkdir(); + writeSignatureFile(); + } else { + File[] children = tableDirectory.listFiles(); + if (children == null || children.length == 0) { + throw new IllegalArgumentException(String.format("table directory: %s is empty", + tableDirectory.getAbsolutePath())); + } + } + } + + private void writeSignatureFile() throws IOException { + File tableDirectory = new File(getParentDirectory(), getName()); + File signatureFile = new File(tableDirectory, DatabaseTableProvider.SIGNATURE_FILE); + signatureFile.createNewFile(); + BufferedWriter writer = new BufferedWriter(new FileWriter(signatureFile)); + List formattedColumnTypes = LocalUtils.formatColumnTypes(columnTypes); + String signature = LocalUtils.join(formattedColumnTypes); + writer.write(signature); + writer.close(); + } + + + public boolean checkAlienStoreable(Storeable storeable) { + for (int index = 0; index < getColumnsCount(); ++index) { + try { + Object o = storeable.getColumnAt(index); + if (o == null) { + continue; + } + if (!o.getClass().equals(getColumnType(index))) { + return false; + } + } catch (IndexOutOfBoundsException e) { + return false; + } + } + try { + storeable.getColumnAt(getColumnsCount()); + } catch (IndexOutOfBoundsException e) { + return true; + } + return false; + } + + Set rawGetKeys() { + return unchangedOldData.keySet(); + } + + public void isStoreableCorrect(Storeable storeable) { + for (int index = 0; index < getColumnsCount(); ++index) { + try { + LocalUtils.checkValue(storeable.getColumnAt(index), columnTypes.get(index)); + } catch (ParseException e) { + throw new IllegalArgumentException(e); + } + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProvider.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProvider.java new file mode 100644 index 000000000..81d5d5f78 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProvider.java @@ -0,0 +1,267 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GlobalUtils; +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; + + +public class DatabaseTableProvider implements TableProvider { + static final String SIGNATURE_FILE = "signature.tsv"; + private static final String CHECKER = "[0-9A-Za-zА-Яа-я]+"; + private final Lock tableLock = new ReentrantLock(true); + + HashMap tables = new HashMap(); + private String databaseDirPath; + private DatabaseTable currentTable = null; + + public DatabaseTableProvider(String databaseDirPath) { + if (databaseDirPath == null) { + throw new IllegalArgumentException("path to database can not be null"); + } + + this.databaseDirPath = databaseDirPath; + File databaseDir = new File(databaseDirPath); + if (databaseDir.isFile()) { + throw new IllegalArgumentException("database dir cannot be file"); + } + for (File tableFile : databaseDir.listFiles()) { + if (tableFile.isFile()) { + continue; + } + List> columnTypes = readTableSignature(tableFile.getName()); + if (columnTypes == null) { + throw new IllegalArgumentException("table directory is empty"); + } + DatabaseTable table = new DatabaseTable(this, databaseDirPath, tableFile.getName(), + columnTypes); + tables.put(table.getName(), table); + } + } + + public List getTableNames() { + try { + tableLock.lock(); + List toReturnSafe = new LinkedList(tables.keySet()); + return toReturnSafe; + } finally { + tableLock.unlock(); + } + } + + public Table getTable(String name) { + try { + tableLock.lock(); + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("table cannot be null"); + } + checkTableName(name); + DatabaseTable table = tables.get(name); + if (table == null) { + return null; + } + if (currentTable != null && currentTable.getChangesCounter() > 0) { + throw new IllegalStateException(currentTable.getChangesCounter() + " unsaved changes"); + } + currentTable = table; + return table; + } finally { + tableLock.unlock(); + } + } + + public Table createTable(String name, List> columnTypes) throws IOException { + try { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("table's name cannot be null"); + } + checkTableName(name); + if (columnTypes == null || columnTypes.isEmpty()) { + throw new IllegalArgumentException("wrong type ()"); + } + checkColumnTypes(columnTypes); + if (tables.containsKey(name)) { + return null; + } + DatabaseTable table = new DatabaseTable(this, databaseDirPath, name, columnTypes); + tableLock.lock(); + tables.put(name, table); + return table; + } finally { + tableLock.unlock(); + } + } + + public void removeTable(String name) throws IOException { + try { + tableLock.lock(); + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("table's name cannot be null"); + } + + if (!tables.containsKey(name)) { + throw new IllegalStateException(name + " not exists"); + } + if (currentTable != null) { + if (currentTable.getName().equals(name)) { + currentTable = null; + } + } + tables.remove(name); + File tableFile = new File(databaseDirPath, name); + GlobalUtils.deleteFile(tableFile); + } finally { + tableLock.unlock(); + } + } + + public HashMap showTables() { + try { + tableLock.lock(); + HashMap content = new HashMap(); + for (Entry contents : tables.entrySet()) { + content.put(contents.getKey(), contents.getValue().size()); + } + return content; + } finally { + tableLock.unlock(); + } + } + + public String serialize(Table table, Storeable value) throws ColumnFormatException { + if (value == null) { + throw new IllegalArgumentException("value cannot be null"); + } + try { + XmlSerializer xmlSerializer = new XmlSerializer(); + for (int index = 0; index < table.getColumnsCount(); ++index) { + xmlSerializer.write(value.getColumnAt(index)); + } + xmlSerializer.close(); + return xmlSerializer.getRepresentation(); + } catch (IOException e) { + System.err.println(e.getMessage()); + } catch (ParseException e) { + throw new IllegalArgumentException("incorrect value"); + } + return null; + } + + + public Storeable deserialize(Table table, String val) throws ParseException { + if (val == null || val.isEmpty()) { + throw new IllegalArgumentException("value cannot be null or empty"); + } + XmlDeserializer deserializer = new XmlDeserializer(val); + Storeable result = null; + List values = new ArrayList<>(table.getColumnsCount()); + for (int index = 0; index < table.getColumnsCount(); ++index) { + try { + Class expectedType = table.getColumnType(index); + Object columnValue = deserializer.getNext(expectedType); + LocalUtils.checkValue(columnValue, expectedType); + values.add(columnValue); + } catch (ColumnFormatException e) { + throw new ParseException("incompatible type: " + e.getMessage(), index); + } catch (IndexOutOfBoundsException e) { + throw new ParseException("Xml representation doesn't match the format", index); + } + } + try { + deserializer.close(); + result = createFor(table, values); + } catch (ColumnFormatException e) { + throw new ParseException("incompatible types: " + e.getMessage(), 0); + } catch (IndexOutOfBoundsException e) { + throw new ParseException("Xml representation doesn't match the format", 0); + } catch (IOException e) { + throw new ParseException(e.getMessage(), 0); + } + return result; + } + + public Storeable createFor(Table table) { + return rawCreateFor(table); + } + + public Storeable createFor(Table table, List values) + throws ColumnFormatException, IndexOutOfBoundsException { + if (values == null) { + throw new IllegalArgumentException("values cannot be null"); + } + DatabaseRow row = rawCreateFor(table); + row.setColumns(values); + return row; + } + + private DatabaseRow rawCreateFor(Table table) { + DatabaseRow row = new DatabaseRow(); + for (int index = 0; index < table.getColumnsCount(); ++index) { + row.addColumn(table.getColumnType(index)); + } + return row; + } + + + private List> readTableSignature(String tableName) { + File tableDirectory = new File(databaseDirPath, tableName); + File signatureFile = new File(tableDirectory, SIGNATURE_FILE); + String signature = null; + if (!signatureFile.exists()) { + return null; + } + try (BufferedReader reader = new BufferedReader(new FileReader(signatureFile))) { + signature = reader.readLine(); + } catch (IOException e) { + System.err.println("error loading signature file: " + e.getMessage()); + return null; + } + if (signature == null) { + throw new IllegalArgumentException("incorrect signature file"); + } + List> columnTypes = new ArrayList>(); + for (final String columnType : signature.split("\\s+")) { + Class type = StoreableTypes.getTypeByName(columnType); + if (type == null) { + throw new IllegalArgumentException("wrong type (" + columnType + ')'); + } + columnTypes.add(type); + } + return columnTypes; + } + + private boolean checkCorrectTable(File tableDirectory) { + File signatureFile = new File(tableDirectory, SIGNATURE_FILE); + return signatureFile.exists(); + } + + private void checkColumnTypes(List> columnTypes) { + for (final Class columnType : columnTypes) { + if (columnType == null) { + throw new IllegalArgumentException("wrong type ()"); + } + StoreableTypes.getSimpleName(columnType); + } + } + + private void checkTableName(String name) { + if (!name.matches(CHECKER)) { + throw new IllegalArgumentException("Bad symbol!"); + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderFactory.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderFactory.java new file mode 100644 index 000000000..ea5d7ffc1 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderFactory.java @@ -0,0 +1,34 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.File; +import java.io.IOException; + + +import ru.fizteh.fivt.storage.structured.TableProvider; +import ru.fizteh.fivt.storage.structured.TableProviderFactory; + +public class DatabaseTableProviderFactory implements TableProviderFactory { + + public synchronized TableProvider create(String directory) throws IOException { + if (directory == null) { + throw new IllegalArgumentException("directory cannot be null"); + } + + if (directory.trim().isEmpty()) { + throw new IllegalArgumentException("directory's name cannot be empty"); + } + + File databaseDirectory = new File(directory); + if (databaseDirectory.isFile()) { + throw new IllegalArgumentException("cannot create database in file. Provide a directory, please"); + } + + if (!databaseDirectory.exists()) { + if (!databaseDirectory.mkdir()) { + throw new IOException("provider is unavailable"); + } + } + return new DatabaseTableProvider(databaseDirectory.getAbsolutePath()); + } +} + diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderFactoryTest.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderFactoryTest.java new file mode 100644 index 000000000..9fd2c369c --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderFactoryTest.java @@ -0,0 +1,19 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import org.junit.Test; + +import ru.fizteh.fivt.storage.structured.TableProviderFactory; + +import java.io.IOException; + +public class DatabaseTableProviderFactoryTest { + @Test(expected = IllegalArgumentException.class) + public void createProviderNullDirectoryTest() { + TableProviderFactory factory = new DatabaseTableProviderFactory(); + try { + factory.create(null); + } catch (IOException e) { + // + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderTest.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderTest.java new file mode 100644 index 000000000..5cb401948 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableProviderTest.java @@ -0,0 +1,26 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + + +import org.junit.Test; + +import ru.fizteh.fivt.storage.structured.TableProviderFactory; + +import java.io.IOException; + +public class DatabaseTableProviderTest { + TableProviderFactory factory = new DatabaseTableProviderFactory(); + + @Test(expected = IOException.class) + public void createProviderUnavailableShouldFail() throws IOException { + factory.create("M:\\"); + } + + @Test(expected = IllegalArgumentException.class) + public void createProviderEmptyShouldFail() { + try { + factory.create(""); + } catch (IOException e) { + System.out.println("Exception caught"); + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableTest.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableTest.java new file mode 100644 index 000000000..ccc5a53b8 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/DatabaseTableTest.java @@ -0,0 +1,88 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + + +import org.junit.*; + +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; + +public class DatabaseTableTest { + private static final String DATABASE = "C:\\temp\\storeable_test"; + TableProvider provider; + Table currentTable; + + @Before + public void beforeTest() { + DatabaseTableProviderFactory factory = new DatabaseTableProviderFactory(); + try { + provider = factory.create(DATABASE); + } catch (IOException e) { + System.out.println("Exception caught"); + } + + List> columnTypes = new ArrayList<>(); + columnTypes.add(Integer.class); + columnTypes.add(String.class); + try { + currentTable = provider.createTable("testTable", columnTypes); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @After + public void afterTest() { + try { + provider.removeTable("testTable"); + } catch (IOException e) { + // SAD + } + } + + @Test + public void putRemoveShouldNotFail() throws Exception { + currentTable.commit(); + currentTable.put("key1", provider.deserialize(currentTable, getXml(1, "2"))); + currentTable.remove("key1"); + Assert.assertEquals(0, currentTable.commit()); + } + + @Test(expected = ParseException.class) + public void putEmptyValueTest() throws ParseException { + Storeable smth = provider.deserialize(currentTable, getXml(1, "")); + } + + /*@Test(expected = ParseException.class) + public void putNlValueTest() throws ParseException { + Storeable smth = provider.deserialize(currentTable, getXml(1, " ")); + }*/ + + @Test(expected = IllegalArgumentException.class) + public void putNlKeyShouldFail() { + currentTable.put(" ", provider.createFor(currentTable)); + } + + + /*@Test(expected = IllegalArgumentException.class) + public void testPutValueWithWhiteSpaces() + { + Storeable newValue = provider.createFor(currentTable); + DatabaseRow row = (DatabaseRow) newValue; + List values = new ArrayList() {{ + add(1); + add(" "); + }}; + row.setColumns(values); + currentTable.put("somekey", row); + }*/ + + private String getXml(int value1, String value2) { + return String.format("%d%s", value1, value2); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/LocalUtils.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/LocalUtils.java new file mode 100644 index 000000000..91e2f8863 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/LocalUtils.java @@ -0,0 +1,90 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Parser; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Table; + +public class LocalUtils { + public static List parseValues(List valuesRepresentation, Table table) + throws ColumnFormatException { + List values = new ArrayList<>(valuesRepresentation.size() - 1); + + for (int index = 1; index < valuesRepresentation.size(); ++index) { + Object value = StoreableTypes.parseByClass(valuesRepresentation.get(index), + table.getColumnType(index - 1)); + values.add(value); + } + return values; + } + + public static String join(List list) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (final Object listEntry : list) { + if (!first) { + sb.append(" "); + } + first = false; + if (listEntry == null) { + sb.append("null"); + } else { + sb.append(listEntry.toString()); + } + } + return sb.toString(); + } + + public static TableInfo parseCreateCommand(String parameters) throws IllegalArgumentException { + parameters = parameters.trim(); + String tableName = parameters.split("\\s+")[0]; + parameters = parameters.replaceAll("\\s+", " "); + int spaceIndex = parameters.indexOf(' '); + if (spaceIndex == -1) { + throw new IllegalArgumentException("wrong type (no column types)"); + } + String columnTypesString = parameters.substring(spaceIndex).replaceAll("\\((.*)\\)", "$1"); + List columnTypes = Parser.parseCommandArgs(columnTypesString); + + TableInfo info = new TableInfo(tableName); + for (final String columnType : columnTypes) { + info.addColumn(StoreableTypes.getTypeByName(columnType)); + } + + return info; + } + + public static List formatColumnTypes(List> columnTypes) { + List formattedColumnTypes = new ArrayList(); + for (final Class columnType : columnTypes) { + formattedColumnTypes.add(StoreableTypes.getSimpleName(columnType)); + } + return formattedColumnTypes; + } + + public static void checkValue(Object value, Class type) throws ParseException { + if (value == null) { + return; + } + //String t = StoreableTypes.getSimpleName(type); + switch (StoreableTypes.getSimpleName(type)) { + case "String": + String stringValue = (String) value; + /*if (checkStringCorrect(stringValue)) + throw new ParseException("value cannot be null", 0);*/ + break; + default: + break; + } + } + + public static boolean checkStringCorrect(String string) { + + return (string.matches("\\s*") || string.split("\\s+").length != 1); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StorableMain.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StorableMain.java new file mode 100644 index 000000000..fbfca4fc7 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StorableMain.java @@ -0,0 +1,58 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap.CreateCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap.DropCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap.UseCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.CommitCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.ExitCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GetCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.PutCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.RemoveKeyCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.RollbackCommand; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Commands; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.Shell.Shell; +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; + +public class StorableMain { + public static void main(String[] args) { + List> commands = new ArrayList>(); + HashSet> com = new HashSet>() { { + add(new ExitCommand()); + add(new RollbackCommand()); + add(new CommitCommand()); + add(new PutCommand()); + add(new GetCommand()); + add(new RemoveKeyCommand()); + add(new DropCommand()); + add(new UseCommand()); + add(new CreateCommand()); }}; + commands.addAll(com); + HashSet> actualResult = new HashSet>(commands); + Shell shell = new Shell(actualResult); + String databaseDirectory = System.getProperty("fizteh.db.dir"); + + if (databaseDirectory == null) { + System.err.println("You haven't set database directory"); + System.exit(1); + } + + try { + DatabaseTableProviderFactory factory = new DatabaseTableProviderFactory(); + StorableShellState shellState = new StorableShellState(factory.create(databaseDirectory)); + shell.setShellState(shellState); + } catch (IOException e) { + System.err.println("some error occurred during loading"); + System.exit(1); + } catch (IllegalArgumentException e) { + System.err.println("error while loading: " + e.getMessage()); + System.exit(1); + } + shell.run(args, shell); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StorableShellState.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StorableShellState.java new file mode 100644 index 000000000..2b60e8ae2 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StorableShellState.java @@ -0,0 +1,105 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.IOException; + +import ru.fizteh.fivt.storage.structured.Storeable; +import ru.fizteh.fivt.storage.structured.Table; +import ru.fizteh.fivt.storage.structured.TableProvider; + +import java.text.ParseException; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.multifilemap.MultifileMapShellStateInterface; + +public class StorableShellState implements MultifileMapShellStateInterface { + Table table; + TableProvider tableProvider; + + public StorableShellState(TableProvider provider) { + tableProvider = provider; + } + + + public Storeable put(String key, Storeable value) { + return table.put(key, value); + } + + public Storeable get(String key) { + return table.get(key); + } + + public int commit() { + try { + return table.commit(); + } catch (IOException e) { + return -1; + } + } + + public int rollback() { + return table.rollback(); + } + + public int size() { + return table.size(); + } + + public Storeable remove(String key) { + return table.remove(key); + } + + public Table getTable() { + return table; + } + + public String keyToString(String key) { + return key; + } + + public String valueToString(Storeable value) { + String string = tableProvider.serialize(table, value); + return string; + } + + public String parseKey(String key) { + return key; + } + + public Storeable parseValue(String value) { + try { + return tableProvider.deserialize(table, value); + } catch (ParseException e) { + return null; + } + } + + public Table useTable(String name) { + Table temp = tableProvider.getTable(name); + if (temp != null) { + table = temp; + } + return temp; + } + + public Table createTable(String arguments) { + TableInfo info = null; + info = LocalUtils.parseCreateCommand(arguments); + try { + return tableProvider.createTable(info.getName(), info.getColumnsTypes()); + } catch (IOException e) { + return null; + } + } + + public void dropTable(String name) throws IOException { + tableProvider.removeTable(name); + if (table.getName().equals(name)) { + table = null; + } + + } + + public String getCurrentTableName() { + return table.getName(); + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StoreableTableBuilder.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StoreableTableBuilder.java new file mode 100644 index 000000000..39a0d36a1 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StoreableTableBuilder.java @@ -0,0 +1,61 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.File; +import java.text.ParseException; +import java.util.Set; + +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.GlobalUtils; +import ru.fizteh.fivt.students.NikolaiKrivchanskii.filemap.TableBuilder; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; +import ru.fizteh.fivt.storage.structured.Storeable; + +public class StoreableTableBuilder implements TableBuilder { + DatabaseTableProvider provider; + DatabaseTable table; + + private int currentDir; + private int currentFile; + + public StoreableTableBuilder(DatabaseTableProvider provider, DatabaseTable table) { + this.provider = provider; + this.table = table; + } + + public String get(String key) { + Storeable val = table.get(key); + try { + String presentation = provider.serialize(table, val); + return presentation; + } catch (ColumnFormatException e) { + return null; + } + } + + public void put(String key, String value) { + GlobalUtils.checkKeyPlacement(key, currentDir, currentFile); + + Storeable objectValue = null; + try { + objectValue = provider.deserialize(table, value); + } catch (ParseException e) { + System.err.println(e.getMessage()); + } + table.put(key, objectValue); + + } + + public Set getKeys() { + return table.rawGetKeys(); + } + + public File getTableDirectory() { + return new File(table.getParentDirectory(), table.getName()); + } + + public void setCurrentFile(File curFile) { + currentDir = GlobalUtils.parseDirNumber(curFile.getParentFile()); + currentFile = GlobalUtils.parseFileNumber(curFile); + + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StoreableTypes.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StoreableTypes.java new file mode 100644 index 000000000..c04554121 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/StoreableTypes.java @@ -0,0 +1,97 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum StoreableTypes { + + INTEGER("int", Integer.class) { + public Object parseValue(String arg) { + return Integer.parseInt(arg); + } + }, + + LONG("long", Long.class) { + public Object parseValue(String arg) { + return Long.parseLong(arg); + } + }, + + FLOAT("float", Float.class) { + public Object parseValue(String arg) { + return Float.parseFloat(arg); + } + }, + + DOUBLE("double", Double.class) { + public Object parseValue(String arg) { + return Double.parseDouble(arg); + } + }, + + STRING("String", String.class) { + public Object parseValue(String arg) { + return arg; + } + }, + + BYTE("byte", Byte.class) { + public Object parseValue(String arg) { + return Byte.parseByte(arg); + } + }, + + BOOLEAN("boolean", Boolean.class) { + public Object parseValue(String arg) { + return Boolean.parseBoolean(arg); + } + }; + + private final String name; + private final Class type; + + private StoreableTypes(String name, Class type) { + this.name = name; + this.type = type; + } + + private static final Map TYPES_BY_NAME; + private static final Map, StoreableTypes> TYPES_BY_CLASS; + + static { + HashMap tempByName = new HashMap<>(); + HashMap, StoreableTypes> tempByClass = new HashMap<>(); + for (StoreableTypes value : values()) { + tempByName.put(value.name, value); + tempByClass.put(value.type, value); + } + TYPES_BY_NAME = Collections.unmodifiableMap(tempByName); + TYPES_BY_CLASS = Collections.unmodifiableMap(tempByClass); + } + public static Class getTypeByName(String name) { + StoreableTypes formatter = TYPES_BY_NAME.get(name); + if (formatter == null) { + throw new IllegalArgumentException("wrong type (" + name + ')'); + } + return formatter.type; + } + + public static String getSimpleName(Class type) { + StoreableTypes formatter = TYPES_BY_CLASS.get(type); + if (formatter == null) { + throw new IllegalArgumentException("unknown format"); + } + return formatter.name; + } + + public abstract Object parseValue(String string); + + public static Object parseByClass(String string, Class type) { + StoreableTypes formatter = TYPES_BY_CLASS.get(type); + if (formatter == null) { + throw new IllegalArgumentException("wrong type (" + type + ')'); + } + return formatter.parseValue(string); + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/TableInfo.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/TableInfo.java new file mode 100644 index 000000000..86e3666d6 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/TableInfo.java @@ -0,0 +1,27 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.util.ArrayList; +import java.util.List; + +public class TableInfo { + private String name; + private List> typesOfColumns; + + public TableInfo(String name) { + this.name = name; + this.typesOfColumns = new ArrayList>(); + } + + public void addColumn(Class columnType) { + typesOfColumns.add(columnType); + } + + public List> getColumnsTypes() { + return typesOfColumns; + } + + public String getName() { + return name; + } + +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/XmlDeserializer.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/XmlDeserializer.java new file mode 100644 index 000000000..01b182b42 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/XmlDeserializer.java @@ -0,0 +1,82 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.IOException; +import java.io.StringReader; +import java.text.ParseException; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import ru.fizteh.fivt.storage.structured.ColumnFormatException; + +public class XmlDeserializer { + String representation; + XMLStreamReader reader; + + public XmlDeserializer(String representation) throws ParseException { + this.representation = representation; + try { + reader = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(representation)); + if (!reader.hasNext()) { + throw new ParseException("xml presentation is empty", 0); + } + int type = reader.next(); + if (type != XMLStreamConstants.START_ELEMENT) { + throw new ParseException("incorrect xml", 0); + } + if (!reader.getName().getLocalPart().equals("row")) { + throw new ParseException("incorrect xml", 0); + } + + } catch (XMLStreamException e) { + throw new ParseException("error in deserializer: " + e.getMessage(), 0); + } + } + + public Object getNext(Class typeExpected) throws ColumnFormatException, ParseException { + Object val = null; + try { + int type = reader.next(); + String k = reader.getName().getLocalPart(); + if (type != XMLStreamConstants.START_ELEMENT || !k.equals("col")) { + if (type == XMLStreamConstants.START_ELEMENT && k.equals("null")) { + reader.next(); + return val; + } + throw new ParseException("incorrect xml", 0); + } + type = reader.next(); + if (type == XMLStreamConstants.CHARACTERS) { + val = StoreableTypes.parseByClass(reader.getText(), typeExpected); + } else { + if (!reader.getName().getLocalPart().equals("null")) { + throw new ParseException("incorrect xml", 0); + } + val = null; + type = reader.next(); + if (type != XMLStreamConstants.END_ELEMENT) { + throw new ParseException("incorrect xml", 0); + } + } + type = reader.next(); + if (type != XMLStreamConstants.END_ELEMENT) { + throw new ParseException("incorrect xml", 0); + } + } catch (XMLStreamException e) { + throw new ParseException("error in deserializer: " + e.getMessage(), 0); + } + return val; + } + + public void close() throws ParseException, IOException { + try { + int type = reader.next(); + if (type != XMLStreamConstants.END_ELEMENT && type != XMLStreamConstants.END_DOCUMENT) { + throw new ParseException("incorrect xml", 0); + } + } catch (XMLStreamException e) { + throw new IOException("error while deserializing: " + e.getMessage()); + } + } +} diff --git a/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/XmlSerializer.java b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/XmlSerializer.java new file mode 100644 index 000000000..ca913ce56 --- /dev/null +++ b/src/ru/fizteh/fivt/students/NikolaiKrivchanskii/storable/XmlSerializer.java @@ -0,0 +1,52 @@ +package ru.fizteh.fivt.students.NikolaiKrivchanskii.storable; + +import java.io.IOException; +import java.io.StringWriter; +import java.text.ParseException; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +public class XmlSerializer { + StringWriter strWriter = new StringWriter(); + XMLStreamWriter writer = null; + + public XmlSerializer() throws IOException { + XMLOutputFactory factory = XMLOutputFactory.newInstance(); + try { + writer = factory.createXMLStreamWriter(strWriter); + writer.writeStartElement("row"); + } catch (XMLStreamException e) { + throw new IOException("error while serializing: " + e.getMessage()); + } + } + + public void write(Object value) throws IOException, ParseException { + try { + writer.writeStartElement("col"); + if (value == null) { + writer.writeStartElement("null"); + writer.writeEndElement(); + } else { + writer.writeCharacters(value.toString()); + } + writer.writeEndElement(); + } catch (XMLStreamException e) { + throw new IOException("error while serializing: " + e.getMessage()); + } + } + + public void close() throws IOException { + try { + writer.writeEndElement(); + writer.flush(); + } catch (XMLStreamException e) { + throw new IOException("error while serializing: " + e.getMessage()); + } + } + + public String getRepresentation() { + return strWriter.getBuffer().toString(); + } +}