improve exception handling on syntax errors

Resolves #167
Resolves #177
This commit is contained in:
marhali 2022-10-14 17:36:01 +02:00
parent 38f5bcb900
commit 150f40d395
11 changed files with 138 additions and 9 deletions

View File

@ -3,6 +3,9 @@
# easy-i18n Changelog
## [Unreleased]
### Changed
- Improved exception handling on syntax errors
### Fixed
- Some settings are not retained on IDE restarts

View File

@ -0,0 +1,37 @@
package de.marhali.easyi18n.action;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.ResourceBundle;
/**
* Plugin action to open a specific file.
* @author marhali
*/
public class OpenFileAction extends AnAction {
private final VirtualFile file;
public OpenFileAction(VirtualFile file) {
this(file, true);
}
public OpenFileAction(VirtualFile file, boolean showIcon) {
super(ResourceBundle.getBundle("messages").getString("action.file"),
null, showIcon ? AllIcons.FileTypes.Any_type : null);
this.file = file;
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = Objects.requireNonNull(e.getProject());
FileEditorManager.getInstance(project).openFile(file, true, true);
}
}

View File

@ -18,8 +18,12 @@ import java.util.ResourceBundle;
public class SettingsAction extends AnAction {
public SettingsAction() {
this(true);
}
public SettingsAction(boolean showIcon) {
super(ResourceBundle.getBundle("messages").getString("action.settings"),
null, AllIcons.General.Settings);
null, showIcon ? AllIcons.General.Settings : null);
}
@Override

View File

@ -0,0 +1,20 @@
package de.marhali.easyi18n.exception;
import de.marhali.easyi18n.model.TranslationFile;
/**
* Indicates a syntax error in a managed translation file.
* @author marhali
*/
public class SyntaxException extends RuntimeException {
private final TranslationFile file;
public SyntaxException(String message, TranslationFile file) {
super(message);
this.file = file;
}
public TranslationFile getFile() {
return file;
}
}

View File

@ -10,12 +10,14 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import de.marhali.easyi18n.exception.EmptyLocalesDirException;
import de.marhali.easyi18n.exception.SyntaxException;
import de.marhali.easyi18n.io.folder.FolderStrategy;
import de.marhali.easyi18n.io.parser.ParserStrategy;
import de.marhali.easyi18n.io.parser.ParserStrategyType;
import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.settings.ProjectSettings;
import de.marhali.easyi18n.util.NotificationHelper;
import org.jetbrains.annotations.NotNull;
import java.io.File;
@ -73,6 +75,8 @@ public class IOHandler {
for(TranslationFile file : translationFiles) {
try {
this.parserStrategy.read(file, data);
} catch (SyntaxException ex) {
NotificationHelper.createBadSyntaxNotification(project, ex);
} catch(Exception ex) {
throw new IOException(file + "\n\n" + ex.getMessage(), ex);
}

View File

@ -4,8 +4,10 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.exception.SyntaxException;
import de.marhali.easyi18n.io.parser.ParserStrategy;
import de.marhali.easyi18n.model.*;
import de.marhali.easyi18n.settings.ProjectSettings;
@ -36,7 +38,14 @@ public class JsonParserStrategy extends ParserStrategy {
TranslationNode targetNode = super.getOrCreateTargetNode(file, data);
try(Reader reader = new InputStreamReader(vf.getInputStream(), vf.getCharset())) {
JsonObject input = GSON.fromJson(reader, JsonObject.class);
JsonObject input;
try {
input = GSON.fromJson(reader, JsonObject.class);
} catch (JsonSyntaxException ex) {
throw new SyntaxException(ex.getMessage(), file);
}
if(input != null) { // @input is null if file is completely empty
JsonMapper.read(file.getLocale(), input, targetNode);
}

View File

@ -2,6 +2,7 @@ package de.marhali.easyi18n.io.parser.json5;
import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.exception.SyntaxException;
import de.marhali.easyi18n.io.parser.ParserStrategy;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationFile;
@ -11,6 +12,7 @@ import de.marhali.json5.Json5;
import de.marhali.json5.Json5Element;
import de.marhali.json5.Json5Object;
import de.marhali.json5.exception.Json5Exception;
import org.jetbrains.annotations.NotNull;
import java.io.InputStreamReader;
@ -38,8 +40,18 @@ public class Json5ParserStrategy extends ParserStrategy {
TranslationNode targetNode = super.getOrCreateTargetNode(file, data);
try (Reader reader = new InputStreamReader(vf.getInputStream(), vf.getCharset())) {
Json5Element input = JSON5.parse(reader);
if(input != null && input.isJson5Object()) {
Json5Element input;
try {
input = JSON5.parse(reader);
if(input != null && !input.isJson5Object()) {
throw new SyntaxException("Expected a json5 object as root node", file);
}
} catch (Json5Exception ex) {
throw new SyntaxException(ex.getMessage(), file);
}
if(input != null) {
Json5Mapper.read(file.getLocale(), input.getAsJson5Object(), targetNode);
}
}

View File

@ -2,6 +2,7 @@ package de.marhali.easyi18n.io.parser.properties;
import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.exception.SyntaxException;
import de.marhali.easyi18n.io.parser.ParserStrategy;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationFile;
@ -11,6 +12,7 @@ import de.marhali.easyi18n.util.KeyPathConverter;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
@ -38,8 +40,16 @@ public class PropertiesParserStrategy extends ParserStrategy {
try(Reader reader = new InputStreamReader(vf.getInputStream(), vf.getCharset())) {
SortableProperties input = new SortableProperties(this.settings.isSorting());
input.load(reader);
PropertiesMapper.read(file.getLocale(), input, targetData, converter);
try {
input.load(reader);
} catch(IOException ex) {
throw new SyntaxException(ex.getMessage(), file);
}
if(!input.isEmpty()) {
PropertiesMapper.read(file.getLocale(), input, targetData, converter);
}
}
}

View File

@ -2,6 +2,7 @@ package de.marhali.easyi18n.io.parser.yaml;
import com.intellij.openapi.vfs.VirtualFile;
import de.marhali.easyi18n.exception.SyntaxException;
import de.marhali.easyi18n.io.parser.ParserStrategy;
import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationFile;
@ -10,6 +11,7 @@ import de.marhali.easyi18n.settings.ProjectSettings;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.error.YAMLException;
import thito.nodeflow.config.MapSection;
import thito.nodeflow.config.Section;
@ -34,7 +36,14 @@ public class YamlParserStrategy extends ParserStrategy {
TranslationNode targetNode = super.getOrCreateTargetNode(file, data);
try(Reader reader = new InputStreamReader(vf.getInputStream(), vf.getCharset())) {
Section input = Section.parseToMap(reader);
Section input;
try {
input = Section.parseToMap(reader);
} catch (YAMLException ex) {
throw new SyntaxException(ex.getMessage(), file);
}
YamlMapper.read(file.getLocale(), input, targetNode);
}
}

View File

@ -3,7 +3,9 @@ package de.marhali.easyi18n.util;
import com.intellij.notification.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import de.marhali.easyi18n.action.OpenFileAction;
import de.marhali.easyi18n.action.SettingsAction;
import de.marhali.easyi18n.exception.SyntaxException;
import de.marhali.easyi18n.io.IOHandler;
import de.marhali.easyi18n.settings.ProjectSettings;
import org.jetbrains.annotations.NotNull;
@ -16,6 +18,7 @@ import java.util.ResourceBundle;
* @author marhali
*/
public class NotificationHelper {
private static final String NOTIFICATION_GROUP = "Easy I18n Notification Group";
public static void createIOError(@NotNull ProjectSettings state, Exception ex) {
ResourceBundle bundle = ResourceBundle.getBundle("messages");
@ -26,11 +29,27 @@ public class NotificationHelper {
Logger.getInstance(IOHandler.class).error(message, ex);
}
public static void createBadSyntaxNotification(Project project, SyntaxException ex) {
ResourceBundle bundle = ResourceBundle.getBundle("messages");
Notification notification = new Notification(
NOTIFICATION_GROUP,
bundle.getString("warning.bad-syntax"),
ex.getMessage(),
NotificationType.ERROR
);
notification.addAction(new OpenFileAction(ex.getFile().getVirtualFile(), false));
notification.addAction(new SettingsAction(false));
Notifications.Bus.notify(notification, project);
}
public static void createEmptyLocalesDirNotification(Project project) {
ResourceBundle bundle = ResourceBundle.getBundle("messages");
Notification notification = new Notification(
"Easy I18n Notification Group",
NOTIFICATION_GROUP,
"Easy I18n",
bundle.getString("warning.missing-config"),
NotificationType.WARNING);

View File

@ -13,6 +13,7 @@ action.settings=Settings
action.search=Search...
action.delete=Delete
action.extract=Extract translation
action.file=Open file
translation.key=Key
translation.locales=Locales
# Settings
@ -64,4 +65,5 @@ error.io=An error occurred while processing translation files. \n\
Path: {3} \n\
Please check examples at https://github.com/marhali/easy-i18n before reporting an issue!
error.submit=Open Issue
warning.missing-config=Configure your local project structure
warning.missing-config=Configure your local project structure
warning.bad-syntax=Syntax error in translation file