diff --git a/src/main/java/de/marhali/easyi18n/DataStore.java b/src/main/java/de/marhali/easyi18n/DataStore.java index a255b47..a1e3dd3 100644 --- a/src/main/java/de/marhali/easyi18n/DataStore.java +++ b/src/main/java/de/marhali/easyi18n/DataStore.java @@ -1,24 +1,19 @@ package de.marhali.easyi18n; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.*; -import de.marhali.easyi18n.io.IOStrategy; -import de.marhali.easyi18n.io.json.JsonIOStrategy; -import de.marhali.easyi18n.io.json.ModularizedJsonIOStrategy; -import de.marhali.easyi18n.io.properties.PropertiesIOStrategy; -import de.marhali.easyi18n.io.yaml.YamlIOStrategy; +import de.marhali.easyi18n.ionext.IOHandler; import de.marhali.easyi18n.model.SettingsState; import de.marhali.easyi18n.model.TranslationData; import de.marhali.easyi18n.service.FileChangeListener; import de.marhali.easyi18n.service.SettingsService; +import de.marhali.easyi18n.util.NotificationHelper; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Set; import java.util.function.Consumer; /** @@ -28,13 +23,6 @@ import java.util.function.Consumer; */ public class DataStore { - private static final Set STRATEGIES = new LinkedHashSet<>(Arrays.asList( - new JsonIOStrategy("json"), new ModularizedJsonIOStrategy("json"), - new JsonIOStrategy("arb"), new ModularizedJsonIOStrategy("arb"), - new YamlIOStrategy("yaml"), new YamlIOStrategy("yml"), - new PropertiesIOStrategy() - )); - private final @NotNull Project project; private final @NotNull FileChangeListener changeListener; @@ -59,24 +47,21 @@ public class DataStore { * @param successResult Consumer will inform if operation was successful */ public void loadFromPersistenceLayer(@NotNull Consumer successResult) { - SettingsState state = SettingsService.getInstance(this.project).getState(); - String localesPath = state.getLocalesPath(); + SettingsState settings = SettingsService.getInstance(this.project).getState(); - if(localesPath == null || localesPath.isEmpty()) { // Populate empty instance - this.data = new TranslationData(state.isSortKeys()); - return; - } + ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added) - this.changeListener.updateLocalesPath(localesPath); + ApplicationManager.getApplication().runReadAction(() -> { + try { + this.data = new IOHandler(settings).read(); + this.changeListener.updateLocalesPath(settings.getLocalesPath()); + successResult.accept(true); - IOStrategy strategy = this.determineStrategy(state, localesPath); - - strategy.read(this.project, localesPath, state, (data) -> { - this.data = data == null - ? new TranslationData(state.isSortKeys()) - : data; - - successResult.accept(data != null); + } catch (Exception ex) { + this.data = new TranslationData(settings.isSortKeys()); + successResult.accept(false); + NotificationHelper.createIOError(settings, ex); + } }); } @@ -85,35 +70,17 @@ public class DataStore { * @param successResult Consumer will inform if operation was successful */ public void saveToPersistenceLayer(@NotNull Consumer successResult) { - SettingsState state = SettingsService.getInstance(this.project).getState(); - String localesPath = state.getLocalesPath(); + SettingsState settings = SettingsService.getInstance(this.project).getState(); - if(localesPath == null || localesPath.isEmpty()) { // Cannot save without valid path - successResult.accept(false); - return; - } + ApplicationManager.getApplication().runWriteAction(() -> { + try { + new IOHandler(settings).write(this.data); + successResult.accept(true); - IOStrategy strategy = this.determineStrategy(state, localesPath); - - strategy.write(this.project, localesPath, state, this.data, successResult); - } - - /** - * Chooses the right strategy for the opened project. An exception might be thrown on - * runtime if the project configuration (e.g. locale files does not fit in any strategy). - * @param state Plugin configuration - * @param localesPath Locales directory - * @return matching {@link IOStrategy} - */ - public @NotNull IOStrategy determineStrategy(@NotNull SettingsState state, @NotNull String localesPath) { - for(IOStrategy strategy : STRATEGIES) { - if(strategy.canUse(this.project, localesPath, state)) { - return strategy; + } catch (Exception ex) { + successResult.accept(false); + NotificationHelper.createIOError(settings, ex); } - } - - throw new IllegalArgumentException("Could not determine i18n strategy. " + - "At least one locale file must be defined. " + - "For examples please visit https://github.com/marhali/easy-i18n"); + }); } } \ No newline at end of file diff --git a/src/main/java/de/marhali/easyi18n/ionext/IOHandler.java b/src/main/java/de/marhali/easyi18n/ionext/IOHandler.java new file mode 100644 index 0000000..d076d25 --- /dev/null +++ b/src/main/java/de/marhali/easyi18n/ionext/IOHandler.java @@ -0,0 +1,94 @@ +package de.marhali.easyi18n.ionext; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; + +import de.marhali.easyi18n.ionext.folder.FolderStrategy; +import de.marhali.easyi18n.ionext.parser.ParserStrategy; +import de.marhali.easyi18n.ionext.parser.ParserStrategyType; +import de.marhali.easyi18n.model.*; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; + +/** + * Central component for IO operations based on the configured strategies. + * @author marhali + */ +public class IOHandler { + + private final @NotNull SettingsState settings; + + private final @NotNull FolderStrategy folderStrategy; + + private final @NotNull ParserStrategyType parserStrategyType; + private final @NotNull ParserStrategy parserStrategy; + + public IOHandler(@NotNull SettingsState settings) throws Exception { + + this.settings = settings; + + this.folderStrategy = settings.getFolderStrategy().getStrategy() + .getDeclaredConstructor(SettingsState.class).newInstance(settings); + + this.parserStrategyType = settings.getParserStrategy(); + this.parserStrategy = parserStrategyType.getStrategy() + .getDeclaredConstructor(SettingsState.class).newInstance(settings); + + Logger.getInstance(IOHandler.class).debug("Using: ", + settings.getFolderStrategy(), settings.getParserStrategy(), settings.getFilePattern()); + } + + /** + * Reads translation files from the local project into our data structure.
+ * Note: This method needs to be called from a Read-Action-Context (see ApplicationManager) + * @return Translation data based on the configured strategies + * @throws Exception Could not read translation data + */ + public @NotNull TranslationData read() throws Exception { + String localesPath = this.settings.getLocalesPath(); + + if(localesPath == null || localesPath.isEmpty()) { + throw new IllegalArgumentException("Locales path must not be empty"); + } + + VirtualFile localesDirectory = LocalFileSystem.getInstance().findFileByIoFile(new File(localesPath)); + + if(localesDirectory == null || !localesDirectory.isDirectory()) { + throw new IllegalArgumentException("Specified locales path is invalid (" + localesPath + ")"); + } + + TranslationData data = new TranslationData(this.settings.isSortKeys()); + List translationFiles = this.folderStrategy.analyzeFolderStructure(localesDirectory); + + for(TranslationFile file : translationFiles) { + this.parserStrategy.read(file, data); + } + + return data; + } + + /** + * Writes the provided translation data to the local project files
+ * Note: This method must be called from an Write-Action-Context (see ApplicationManager) + * @param data Cached translation data to save + * @throws Exception Write action failed + */ + public void write(@NotNull TranslationData data) throws Exception { + String localesPath = this.settings.getLocalesPath(); + + if(localesPath == null || localesPath.isEmpty()) { + throw new IllegalArgumentException("Locales path must not be empty"); + } + + List translationFiles = + this.folderStrategy.constructFolderStructure(localesPath, this.parserStrategyType, data); + + for(TranslationFile file : translationFiles) { + this.parserStrategy.write(data, file); + } + } +}