diff --git a/src/seedu/addressbook/logic/Logic.java b/src/seedu/addressbook/logic/Logic.java index 17afd61a0..0ed2efced 100644 --- a/src/seedu/addressbook/logic/Logic.java +++ b/src/seedu/addressbook/logic/Logic.java @@ -5,6 +5,7 @@ import seedu.addressbook.data.AddressBook; import seedu.addressbook.data.person.ReadOnlyPerson; import seedu.addressbook.parser.Parser; +import seedu.addressbook.storage.Storage; import seedu.addressbook.storage.StorageFile; import java.util.Collections; @@ -17,7 +18,7 @@ public class Logic { - private StorageFile storage; + private Storage storage; private AddressBook addressBook; /** The list of person shown to the user most recently. */ @@ -28,12 +29,12 @@ public Logic() throws Exception{ setAddressBook(storage.load()); } - Logic(StorageFile storageFile, AddressBook addressBook){ - setStorage(storageFile); + Logic(Storage storage, AddressBook addressBook){ + setStorage(storage); setAddressBook(addressBook); } - void setStorage(StorageFile storage){ + void setStorage(Storage storage){ this.storage = storage; } @@ -43,13 +44,13 @@ void setAddressBook(AddressBook addressBook){ /** * Creates the StorageFile object based on the user specified path (if any) or the default storage path. - * @throws StorageFile.InvalidStorageFilePathException if the target file path is incorrect. + * @throws Storage.InvalidStorageFilePathException if the target file path is incorrect. */ - private StorageFile initializeStorage() throws StorageFile.InvalidStorageFilePathException { + private Storage initializeStorage() throws Storage.InvalidStorageFilePathException { return new StorageFile(); } - public String getStorageFilePath() { + public String getStoragePath() { return storage.getPath(); } diff --git a/src/seedu/addressbook/storage/Storage.java b/src/seedu/addressbook/storage/Storage.java new file mode 100644 index 000000000..d722ef3f3 --- /dev/null +++ b/src/seedu/addressbook/storage/Storage.java @@ -0,0 +1,29 @@ +package seedu.addressbook.storage; + +import seedu.addressbook.data.AddressBook; +import seedu.addressbook.data.exception.IllegalValueException; + +public abstract class Storage { + + public abstract void save(AddressBook addressBook) throws StorageOperationException ; + + public abstract AddressBook load() throws StorageOperationException; + + public abstract String getPath(); + + /** + * Signals that some error has occured while trying to convert and read/write data between the application + * and the storage file. + */ + public static class StorageOperationException extends Exception { + public StorageOperationException(String message) { + super(message); + } + } + + public static class InvalidStorageFilePathException extends IllegalValueException { + public InvalidStorageFilePathException(String message) { + super(message); + } + } +} diff --git a/src/seedu/addressbook/storage/StorageFile.java b/src/seedu/addressbook/storage/StorageFile.java index 693097a86..d71fdbaa0 100644 --- a/src/seedu/addressbook/storage/StorageFile.java +++ b/src/seedu/addressbook/storage/StorageFile.java @@ -15,7 +15,7 @@ /** * Represents the file used to store address book data. */ -public class StorageFile { +public class StorageFile extends Storage{ /** Default file path used if the user doesn't provide the file name. */ public static final String DEFAULT_STORAGE_FILEPATH = "addressbook.txt"; @@ -24,25 +24,6 @@ public class StorageFile { * More info https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html */ - /** - * Signals that the given file path does not fulfill the storage filepath constraints. - */ - public static class InvalidStorageFilePathException extends IllegalValueException { - public InvalidStorageFilePathException(String message) { - super(message); - } - } - - /** - * Signals that some error has occured while trying to convert and read/write data between the application - * and the storage file. - */ - public static class StorageOperationException extends Exception { - public StorageOperationException(String message) { - super(message); - } - } - private final JAXBContext jaxbContext; public final Path path; @@ -83,6 +64,7 @@ private static boolean isValidPath(Path filePath) { * * @throws StorageOperationException if there were errors converting and/or storing data to file. */ + @Override public void save(AddressBook addressBook) throws StorageOperationException { /* Note: Note the 'try with resource' statement below. @@ -108,6 +90,7 @@ public void save(AddressBook addressBook) throws StorageOperationException { * * @throws StorageOperationException if there were errors reading and/or converting data from file. */ + @Override public AddressBook load() throws StorageOperationException { try (final Reader fileReader = new BufferedReader(new FileReader(path.toFile()))) { @@ -140,7 +123,8 @@ public AddressBook load() throws StorageOperationException { throw new StorageOperationException("File contains illegal data values; data type constraints not met"); } } - + + @Override public String getPath() { return path.toString(); } diff --git a/src/seedu/addressbook/storage/StorageStub.java b/src/seedu/addressbook/storage/StorageStub.java new file mode 100644 index 000000000..160810be7 --- /dev/null +++ b/src/seedu/addressbook/storage/StorageStub.java @@ -0,0 +1,120 @@ +package seedu.addressbook.storage; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.nio.file.Path; +import java.nio.file.Paths; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; + +import seedu.addressbook.data.AddressBook; +import seedu.addressbook.data.exception.IllegalValueException; +import seedu.addressbook.storage.Storage.InvalidStorageFilePathException; +import seedu.addressbook.storage.jaxb.AdaptedAddressBook; + +public class StorageStub extends Storage{ + /** Default file path used if the user doesn't provide the file name. */ + public static final String DEFAULT_STORAGE_FILEPATH = "addressbook.txt"; + + /* Note: Note the use of nested classes below. + * More info https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html + */ + + private final JAXBContext jaxbContext; + + public final Path path; + + /** + * @throws InvalidStorageFilePathException if the default path is invalid + */ + public StorageStub() throws InvalidStorageFilePathException { + this(DEFAULT_STORAGE_FILEPATH); + } + + /** + * @throws InvalidStorageFilePathException if the given file path is invalid + */ + public StorageStub(String filePath) throws InvalidStorageFilePathException { + try { + jaxbContext = JAXBContext.newInstance(AdaptedAddressBook.class); + } catch (JAXBException jaxbe) { + throw new RuntimeException("jaxb initialisation error"); + } + + path = Paths.get(filePath); + if (!isValidPath(path)) { + throw new InvalidStorageFilePathException("Storage file should end with '.txt'"); + } + } + + /** + * Returns true if the given path is acceptable as a storage file. + * The file path is considered acceptable if it ends with '.txt' + */ + private static boolean isValidPath(Path filePath) { + return filePath.toString().endsWith(".txt"); + } + + /** + * Saves all data to this storage file. + * + * @throws StorageOperationException if there were errors converting and/or storing data to file. + */ + @Override + public void save(AddressBook addressBook) throws StorageOperationException { + + } + + /** + * Loads data from this storage file. + * + * @throws StorageOperationException if there were errors reading and/or converting data from file. + */ + @Override + public AddressBook load() throws StorageOperationException { + try (final Reader fileReader = + new BufferedReader(new FileReader(path.toFile()))) { + + final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + final AdaptedAddressBook loaded = (AdaptedAddressBook) unmarshaller.unmarshal(fileReader); + // manual check for missing elements + if (loaded.isAnyRequiredFieldMissing()) { + throw new StorageOperationException("File data missing some elements"); + } + return loaded.toModelType(); + + /* Note: Here, we are using an exception to create the file if it is missing. However, we should minimize + * using exceptions to facilitate normal paths of execution. If we consider the missing file as a 'normal' + * situation (i.e. not truly exceptional) we should not use an exception to handle it. + */ + + // create empty file if not found + } catch (FileNotFoundException fnfe) { + final AddressBook empty = new AddressBook(); + save(empty); + return empty; + + // other errors + } catch (IOException ioe) { + throw new StorageOperationException("Error writing to file: " + path); + } catch (JAXBException jaxbe) { + throw new StorageOperationException("Error parsing file data format"); + } catch (IllegalValueException ive) { + throw new StorageOperationException("File contains illegal data values; data type constraints not met"); + } + } + + @Override + public String getPath() { + return path.toString(); + } +} diff --git a/src/seedu/addressbook/ui/Gui.java b/src/seedu/addressbook/ui/Gui.java index ed21989a4..396120b8f 100644 --- a/src/seedu/addressbook/ui/Gui.java +++ b/src/seedu/addressbook/ui/Gui.java @@ -31,7 +31,7 @@ public Gui(Logic logic, String version) { public void start(Stage stage, Stoppable mainApp) throws IOException { mainWindow = createMainWindow(stage, mainApp); - mainWindow.displayWelcomeMessage(version, logic.getStorageFilePath()); + mainWindow.displayWelcomeMessage(version, logic.getStoragePath()); } private MainWindow createMainWindow(Stage stage, Stoppable mainApp) throws IOException{ diff --git a/test/java/seedu/addressbook/logic/LogicTest.java b/test/java/seedu/addressbook/logic/LogicTest.java index 7efa921ca..971a6284a 100644 --- a/test/java/seedu/addressbook/logic/LogicTest.java +++ b/test/java/seedu/addressbook/logic/LogicTest.java @@ -13,6 +13,7 @@ import seedu.addressbook.data.tag.Tag; import seedu.addressbook.data.tag.UniqueTagList; import seedu.addressbook.storage.StorageFile; +import seedu.addressbook.storage.StorageStub; import java.util.*; @@ -28,16 +29,16 @@ public class LogicTest { @Rule public TemporaryFolder saveFolder = new TemporaryFolder(); - private StorageFile saveFile; + private StorageStub saveStub; private AddressBook addressBook; private Logic logic; @Before public void setup() throws Exception { - saveFile = new StorageFile(saveFolder.newFile("testSaveFile.txt").getPath()); + saveStub = new StorageStub(saveFolder.newFile("testSaveFile.txt").getPath()); addressBook = new AddressBook(); - saveFile.save(addressBook); - logic = new Logic(saveFile, addressBook); + saveStub.save(addressBook); + logic = new Logic(saveStub, addressBook); } @Test @@ -90,7 +91,7 @@ private void assertCommandBehavior(String inputCommand, //Confirm the state of data is as expected assertEquals(expectedAddressBook, addressBook); assertEquals(lastShownList, logic.getLastShownList()); - assertEquals(addressBook, saveFile.load()); + assertEquals(addressBook, saveStub.load()); } diff --git a/test/java/seedu/addressbook/storage/StorageFileTest.java b/test/java/seedu/addressbook/storage/StorageFileTest.java index 1bd6fcab3..a321670c5 100644 --- a/test/java/seedu/addressbook/storage/StorageFileTest.java +++ b/test/java/seedu/addressbook/storage/StorageFileTest.java @@ -17,7 +17,7 @@ import seedu.addressbook.data.person.Phone; import seedu.addressbook.data.tag.Tag; import seedu.addressbook.data.tag.UniqueTagList; -import seedu.addressbook.storage.StorageFile.StorageOperationException; +import seedu.addressbook.storage.Storage.StorageOperationException; import static seedu.addressbook.util.TestUtil.assertTextFilesEqual; public class StorageFileTest {