Skip to content

Commit

Permalink
introduce interface DataDirSupplier
Browse files Browse the repository at this point in the history
Prepare to introducing custom data dir config setting by avoiding
passing around the `Path dataDir` variable -- pass around an object of
type that holds the current data dir setting.

The actual setting will be stored in class `MainGui`.
  • Loading branch information
rybak committed Jul 9, 2022
1 parent b62aa23 commit d7b4b59
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 35 deletions.
59 changes: 39 additions & 20 deletions src/main/java/dev/andrybak/resoday/YearHistory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.andrybak.resoday;

import com.google.gson.JsonParseException;
import dev.andrybak.resoday.gui.settings.DataDirSupplier;
import dev.andrybak.resoday.storage.HabitFiles;
import dev.andrybak.resoday.storage.SerializableYearHistory;
import dev.andrybak.resoday.storage.SerializableYearHistoryV1;
Expand Down Expand Up @@ -36,14 +37,18 @@ public final class YearHistory {
private final Set<LocalDate> dates;
private final List<YearHistoryListener> listeners = new ArrayList<>();
private final String id;
/**
* Gives the parent directory for where to store the habit file.
*/
private final DataDirSupplier dataDirSupplier;
/**
* User-visible name of this habit history.
*/
private String name;
/**
* Where this history is stored in serialized form.
*/
private Path statePath;
private Path relativeStatePath;
/**
* Whether this history is visible in the GUI.
*/
Expand All @@ -55,35 +60,42 @@ public final class YearHistory {
*/
private boolean hasChanges = true;

public YearHistory(Path statePath, String name, String id) {
this(statePath, emptySet(), name, id, Visibility.VISIBLE);
public YearHistory(DataDirSupplier dataDirSupplier, Path relativeStatePath, String name, String id) {
this(dataDirSupplier, relativeStatePath, emptySet(), name, id, Visibility.VISIBLE);
}

private YearHistory(Path statePath, Collection<LocalDate> dates, String name, String id, Visibility visibility) {
this.statePath = statePath;
private YearHistory(DataDirSupplier dataDirSupplier, Path relativeStatePath, Collection<LocalDate> dates,
String name, String id, Visibility visibility)
{
this.dataDirSupplier = dataDirSupplier;
this.relativeStatePath = relativeStatePath;
this.dates = new HashSet<>(dates);
this.name = name;
this.id = id;
this.visibility = visibility;
}

private YearHistory(Path statePath, SerializableYearHistory serializableYearHistory) {
this(statePath, serializableYearHistory.getDates(), serializableYearHistory.getName(),
serializableYearHistory.getId(), serializableYearHistory.getVisibility());
private YearHistory(DataDirSupplier dataDirSupplier, Path relativeStatePath,
SerializableYearHistory serializableYearHistory)
{
this(dataDirSupplier, relativeStatePath, serializableYearHistory.getDates(),
serializableYearHistory.getName(), serializableYearHistory.getId(),
serializableYearHistory.getVisibility());
}

/**
* Read a habit file of any text-based version. Currently:
* <ul>
* <li>version 0: plain text {@link #readV0(Path)}</li>
* <li>version 0: plain text {@link #readV0(DataDirSupplier, Path)}</li>
* <li>version 1: JSON see {@link SerializableYearHistoryV1}</li>
* <li>version 2: JSON see {@link SerializableYearHistory}</li>
* </ul>
*/
public static Optional<YearHistory> read(Path statePath) {
public static Optional<YearHistory> read(DataDirSupplier dataDirSupplier, Path statePath) {
Path relativeStatePath = dataDirSupplier.getDataDir().relativize(statePath);
if (!Files.exists(statePath)) {
System.out.println("No saved state.");
return Optional.of(new YearHistory(statePath, HabitFiles.pathToName(statePath),
return Optional.of(new YearHistory(dataDirSupplier, relativeStatePath, HabitFiles.pathToName(statePath),
HabitFiles.v0v1PathToId(statePath)));
}
if (!Files.isRegularFile(statePath) || !Files.isReadable(statePath)) {
Expand All @@ -95,13 +107,13 @@ public static Optional<YearHistory> read(Path statePath) {
try (BufferedReader r = Files.newBufferedReader(statePath)) {
String name = HabitFiles.pathToName(statePath);
SerializableYearHistory serializableYearHistory = SerializableYearHistory.fromJson(r, name, statePath);
tmp = new YearHistory(statePath, serializableYearHistory);
tmp = new YearHistory(dataDirSupplier, relativeStatePath, serializableYearHistory);
} catch (IOException e) {
System.err.println("Could not read '" + statePath.toAbsolutePath() + "': " + e);
return Optional.empty();
} catch (JsonParseException e) {
// fallback to version 0 of storage
return readV0(statePath);
return readV0(dataDirSupplier, statePath);
}
if (isV0V1HiddenFile) {
tmp.reHideV0V1File();
Expand All @@ -112,7 +124,7 @@ public static Optional<YearHistory> read(Path statePath) {
/**
* For backward compatibility
*/
private static Optional<YearHistory> readV0(Path statePath) {
private static Optional<YearHistory> readV0(DataDirSupplier dataDirSupplier, Path statePath) {
YearHistory tmp;
final boolean isV0V1HiddenFile = HabitFiles.isV0V1HiddenFile(statePath);
try (Stream<String> lines = Files.lines(statePath)) {
Expand All @@ -127,7 +139,9 @@ private static Optional<YearHistory> readV0(Path statePath) {
})
.filter(Objects::nonNull)
.collect(toList());
tmp = new YearHistory(statePath, dates,
tmp = new YearHistory(dataDirSupplier,
dataDirSupplier.getDataDir().relativize(statePath),
dates,
HabitFiles.pathToName(statePath),
HabitFiles.v0v1PathToId(statePath),
isV0V1HiddenFile ? Visibility.HIDDEN : Visibility.VISIBLE
Expand All @@ -149,6 +163,10 @@ private static String sanitize(String s) {
return s.substring(0, 20);
}

private Path getStatePath() {
return dataDirSupplier.getDataDir().resolve(relativeStatePath);
}

public void turnOn(LocalDate d) {
System.out.println("Turned on " + d);
hasChanges = true;
Expand Down Expand Up @@ -177,6 +195,7 @@ public void save() {
if (!hasChanges) {
return;
}
Path statePath = getStatePath();
try {
System.out.println("\tSaving to '" + statePath.toAbsolutePath() + "'...");
Path tmpFile = Files.createTempFile(statePath.getParent(), "resoday", ".habit.tmp");
Expand All @@ -194,16 +213,16 @@ public void save() {
}

public void reHideV0V1File() {
Path originalHiddenPath = this.statePath;
final Path originalHiddenPath = getStatePath();
System.out.println("Re-hiding '" + originalHiddenPath.toAbsolutePath() + "'...");
Path newPath = HabitFiles.fromV0Hidden(originalHiddenPath);
if (newPath.equals(originalHiddenPath)) {
System.err.println("Warning: trying to re-hide '" + originalHiddenPath.toAbsolutePath() + "'");
return;
}
try {
Files.move(this.statePath, newPath);
this.statePath = newPath;
Files.move(originalHiddenPath, newPath);
relativeStatePath = dataDirSupplier.getDataDir().relativize(newPath);
setVisibility(Visibility.HIDDEN); // it was .hidden before, so override whatever was deserialized
System.out.println("Moved '" + originalHiddenPath.toAbsolutePath() +
"' to '" + newPath.toAbsolutePath() + "'");
Expand All @@ -215,9 +234,9 @@ public void reHideV0V1File() {

public void delete() {
try {
Files.delete(this.statePath);
Files.delete(getStatePath());
} catch (IOException e) {
System.err.println("Could not delete '" + this.statePath.toAbsolutePath() + "': " + e);
System.err.println("Could not delete '" + getStatePath().toAbsolutePath() + "': " + e);
e.printStackTrace();
}
}
Expand Down
9 changes: 6 additions & 3 deletions src/main/java/dev/andrybak/resoday/gui/Histories.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import dev.andrybak.resoday.YearHistory;
import dev.andrybak.resoday.gui.edithabits.ReorderHabitsDialog;
import dev.andrybak.resoday.gui.settings.CalendarLayoutSettingProvider;
import dev.andrybak.resoday.gui.settings.DataDirSupplier;
import dev.andrybak.resoday.storage.SortOrder;

import java.awt.Window;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -49,9 +49,12 @@ public List<HistoryPanel> getOrderedPanels() {
}

/**
* Show {@link ReorderHabitsDialog} and reorder habits in this {@code Histories} according to user inputs.
*
* @param editCallback called, if something was edited
*/
public void reorder(Window parent, Path dir, CalendarLayoutSettingProvider calendarLayoutSettingProvider,
public void reorder(Window parent, DataDirSupplier dataDirSupplier,
CalendarLayoutSettingProvider calendarLayoutSettingProvider,
Runnable editCallback)
{
List<ReorderHabitsDialog.Row> inputRows = new ArrayList<>();
Expand Down Expand Up @@ -101,7 +104,7 @@ public void reorder(Window parent, Path dir, CalendarLayoutSettingProvider calen
.map(ReorderHabitsDialog.Row::getId)
.collect(Collectors.toList());
if (!inputOrder.equals(outputOrder)) {
SortOrder.save(dir, outputOrder);
SortOrder.save(dataDirSupplier, outputOrder);
}
editCallback.run();
});
Expand Down
27 changes: 17 additions & 10 deletions src/main/java/dev/andrybak/resoday/gui/MainGui.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import dev.andrybak.resoday.gui.help.DebugDialog;
import dev.andrybak.resoday.gui.help.HelpDialog;
import dev.andrybak.resoday.gui.settings.CalendarLayoutSettingProvider;
import dev.andrybak.resoday.gui.settings.DataDirSupplier;
import dev.andrybak.resoday.gui.settings.GuiSettingsSaver;
import dev.andrybak.resoday.gui.settings.SettingsMenu;
import dev.andrybak.resoday.settings.gui.CalendarLayoutSetting;
Expand Down Expand Up @@ -66,9 +67,11 @@ public final class MainGui implements CalendarLayoutSettingProvider {
private final Histories histories = new Histories();
private final Timer autoSaveTimer;
private GuiSettings guiSettings;
private final Path dataDir;
private final GuiSettingsSaver guiSettingsSaver = new GuiSettingsSaver();

public MainGui(Path dataDir, Path configDir) {
this.dataDir = dataDir;
content = new JPanel(new BorderLayout());
guiSettings = GuiSettings.read(configDir);

Expand All @@ -78,7 +81,7 @@ public MainGui(Path dataDir, Path configDir) {
.filter(Files::isRegularFile)
.filter(Files::isReadable)
.filter(HabitFiles.IS_HABIT_FILE)
.map(YearHistory::read)
.map(statePath -> YearHistory.read(getDataDirSupplier(), statePath))
.flatMap(Optional::stream)
.collect(Collectors.toMap(YearHistory::getId, Function.identity()));
Optional<SortOrder> maybeOrder = SortOrder.read(dataDir);
Expand Down Expand Up @@ -116,33 +119,37 @@ public MainGui(Path dataDir, Path configDir) {
autoSaveTimer = new Timer(Math.toIntExact(AUTO_SAVE_PERIOD.toMillis()), ignored -> autoSave(configDir));
autoSaveTimer.addActionListener(ignored -> histories.forEachPanel(HistoryPanel::updateDecorations));

setUpMenuBar(dataDir, tabs);
setUpMenuBar(tabs);
}

private static Image getResodayImage() {
URL resodayIconUrl = Objects.requireNonNull(MainGui.class.getResource(APP_ICON_FILENAME));
return Toolkit.getDefaultToolkit().getImage(resodayIconUrl);
}

private DataDirSupplier getDataDirSupplier() {
return () -> dataDir;
}

private void markTodayInCurrentTab(JTabbedPane tabs) {
getCurrentHistoryPanel(tabs).ifPresent(HistoryPanel::markToday);
}

private void setUpMenuBar(Path dir, JTabbedPane tabs) {
private void setUpMenuBar(JTabbedPane tabs) {
JMenuBar menuBar = new JMenuBar();

JMenu mainMenu = new JMenu("Main");
mainMenu.setMnemonic('M');
{
JMenuItem addHabitMenuItem = new JMenuItem("Add habit");
addHabitMenuItem.setMnemonic('A');
addHabitMenuItem.addActionListener(ignored -> openAddHabitDialog(dir, tabs));
addHabitMenuItem.addActionListener(ignored -> openAddHabitDialog(tabs));
mainMenu.add(addHabitMenuItem);
}
{
JMenuItem reorderHabitsMenuItem = new JMenuItem("Reorder habits");
reorderHabitsMenuItem.setMnemonic('O');
reorderHabitsMenuItem.addActionListener(ignored -> openReorderHabitsDialog(dir, tabs));
reorderHabitsMenuItem.addActionListener(ignored -> openReorderHabitsDialog(tabs));
mainMenu.add(reorderHabitsMenuItem);
}
mainMenu.add(new JSeparator());
Expand Down Expand Up @@ -204,18 +211,18 @@ private void setUpMenuBar(Path dir, JTabbedPane tabs) {
window.setJMenuBar(menuBar);
}

private void openAddHabitDialog(Path dir, JTabbedPane tabs) {
private void openAddHabitDialog(JTabbedPane tabs) {
Set<String> names = histories.histories()
.map(YearHistory::getName)
.collect(toSet());
ChooseHabitNameDialog.show(window, "Add habit", "+", null, names::contains, habitName -> {
String newId = HabitFiles.createNewId();
String filename = HabitFiles.createNewFilename(newId, habitName);
Path newHabitPath = dir.resolve(filename);
YearHistory newHistory = new YearHistory(newHabitPath, habitName, newId);
YearHistory newHistory = new YearHistory(getDataDirSupplier(), Path.of(filename), habitName, newId);
HistoryPanel newPanel = new HistoryPanel(newHistory, this);
histories.add(newHistory, newPanel);
tabs.addTab(habitName, newPanel);
Path newHabitPath = dataDir.resolve(filename);
System.out.println("Added new habit '" + habitName + "' at path '" + newHabitPath.toAbsolutePath() + "'.");
});
}
Expand Down Expand Up @@ -296,11 +303,11 @@ private void deleteHabit(JTabbedPane tabs, HistoryPanel historyPanel) {
tabs.removeTabAt(i);
}

private void openReorderHabitsDialog(Path dir, JTabbedPane tabs) {
private void openReorderHabitsDialog(JTabbedPane tabs) {
String oldSelectedId = getCurrentHistoryPanel(tabs)
.map(HistoryPanel::getHistoryId)
.orElse(null);
histories.reorder(this.window, dir, this, () -> {
histories.reorder(this.window, getDataDirSupplier(), this, () -> {
for (int i = 0, n = tabs.getTabCount(); i < n; i++) {
tabs.removeTabAt(0); // removing all tabs
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package dev.andrybak.resoday.gui.settings;

import java.nio.file.Path;

@FunctionalInterface
public interface DataDirSupplier {
Path getDataDir();
}
6 changes: 4 additions & 2 deletions src/main/java/dev/andrybak/resoday/storage/SortOrder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package dev.andrybak.resoday.storage;

import dev.andrybak.resoday.gui.settings.DataDirSupplier;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -23,8 +25,8 @@ public class SortOrder {
this.order = new ArrayList<>(order);
}

public static void save(Path rootDir, List<String> ids) {
Path p = rootDir.resolve(ORDER_FILE);
public static void save(DataDirSupplier dataDirSupplier, List<String> ids) {
Path p = dataDirSupplier.getDataDir().resolve(ORDER_FILE);
try {
Files.write(p, ids);
} catch (IOException e) {
Expand Down

0 comments on commit d7b4b59

Please sign in to comment.