Merge pull request #54 from sunarya-thito/main
Added YAML support and prefix support
This commit is contained in:
commit
e7479b17b9
@ -27,6 +27,7 @@ public class SettingsDialog {
|
|||||||
private TextFieldWithBrowseButton pathText;
|
private TextFieldWithBrowseButton pathText;
|
||||||
private JBTextField filePatternText;
|
private JBTextField filePatternText;
|
||||||
private JBTextField previewText;
|
private JBTextField previewText;
|
||||||
|
private JBTextField prefixText;
|
||||||
private JBCheckBox codeAssistanceCheckbox;
|
private JBCheckBox codeAssistanceCheckbox;
|
||||||
|
|
||||||
public SettingsDialog(Project project) {
|
public SettingsDialog(Project project) {
|
||||||
@ -37,20 +38,22 @@ public class SettingsDialog {
|
|||||||
String localesPath = SettingsService.getInstance(project).getState().getLocalesPath();
|
String localesPath = SettingsService.getInstance(project).getState().getLocalesPath();
|
||||||
String filePattern = SettingsService.getInstance(project).getState().getFilePattern();
|
String filePattern = SettingsService.getInstance(project).getState().getFilePattern();
|
||||||
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
|
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
|
||||||
|
String prefixLocale = SettingsService.getInstance(project).getState().getPrefix();
|
||||||
boolean codeAssistance = SettingsService.getInstance(project).getState().isCodeAssistance();
|
boolean codeAssistance = SettingsService.getInstance(project).getState().isCodeAssistance();
|
||||||
|
|
||||||
if(prepare(localesPath, filePattern, previewLocale, codeAssistance).show() == DialogWrapper.OK_EXIT_CODE) { // Save changes
|
if(prepare(localesPath, filePattern, previewLocale, prefixLocale, codeAssistance).show() == DialogWrapper.OK_EXIT_CODE) { // Save changes
|
||||||
SettingsService.getInstance(project).getState().setLocalesPath(pathText.getText());
|
SettingsService.getInstance(project).getState().setLocalesPath(pathText.getText());
|
||||||
SettingsService.getInstance(project).getState().setFilePattern(filePatternText.getText());
|
SettingsService.getInstance(project).getState().setFilePattern(filePatternText.getText());
|
||||||
SettingsService.getInstance(project).getState().setPreviewLocale(previewText.getText());
|
SettingsService.getInstance(project).getState().setPreviewLocale(previewText.getText());
|
||||||
SettingsService.getInstance(project).getState().setCodeAssistance(codeAssistanceCheckbox.isSelected());
|
SettingsService.getInstance(project).getState().setCodeAssistance(codeAssistanceCheckbox.isSelected());
|
||||||
|
SettingsService.getInstance(project).getState().setPrefix(prefixText.getText());
|
||||||
|
|
||||||
// Reload instance
|
// Reload instance
|
||||||
DataStore.getInstance(project).reloadFromDisk();
|
DataStore.getInstance(project).reloadFromDisk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DialogBuilder prepare(String localesPath, String filePattern, String previewLocale, boolean codeAssistance) {
|
private DialogBuilder prepare(String localesPath, String filePattern, String previewLocale, String prefixLocale, boolean codeAssistance) {
|
||||||
JPanel rootPanel = new JPanel(new GridLayout(0, 1, 2, 2));
|
JPanel rootPanel = new JPanel(new GridLayout(0, 1, 2, 2));
|
||||||
|
|
||||||
JBLabel pathLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("settings.path.text"));
|
JBLabel pathLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("settings.path.text"));
|
||||||
@ -80,6 +83,11 @@ public class SettingsDialog {
|
|||||||
codeAssistanceCheckbox = new JBCheckBox(ResourceBundle.getBundle("messages").getString("settings.editor.assistance"));
|
codeAssistanceCheckbox = new JBCheckBox(ResourceBundle.getBundle("messages").getString("settings.editor.assistance"));
|
||||||
codeAssistanceCheckbox.setSelected(codeAssistance);
|
codeAssistanceCheckbox.setSelected(codeAssistance);
|
||||||
|
|
||||||
|
JBLabel prefixLabel = new JBLabel(ResourceBundle.getBundle("messages").getString("settings.path.prefix"));
|
||||||
|
prefixText = new JBTextField(prefixLocale);
|
||||||
|
rootPanel.add(prefixLabel);
|
||||||
|
rootPanel.add(prefixText);
|
||||||
|
|
||||||
rootPanel.add(codeAssistanceCheckbox);
|
rootPanel.add(codeAssistanceCheckbox);
|
||||||
|
|
||||||
DialogBuilder builder = new DialogBuilder();
|
DialogBuilder builder = new DialogBuilder();
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
package de.marhali.easyi18n.editor;
|
package de.marhali.easyi18n.editor;
|
||||||
|
|
||||||
import com.intellij.codeInsight.completion.CompletionParameters;
|
import com.intellij.codeInsight.completion.*;
|
||||||
import com.intellij.codeInsight.completion.CompletionProvider;
|
import com.intellij.codeInsight.lookup.*;
|
||||||
import com.intellij.codeInsight.completion.CompletionResultSet;
|
import com.intellij.openapi.project.*;
|
||||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
import com.intellij.util.*;
|
||||||
import com.intellij.openapi.project.Project;
|
import de.marhali.easyi18n.model.*;
|
||||||
import com.intellij.util.ProcessingContext;
|
import de.marhali.easyi18n.service.*;
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
import de.marhali.easyi18n.model.LocalizedNode;
|
import java.util.*;
|
||||||
import de.marhali.easyi18n.service.DataStore;
|
|
||||||
import de.marhali.easyi18n.service.SettingsService;
|
|
||||||
import de.marhali.easyi18n.util.TranslationsUtil;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* I18n translation key completion provider.
|
* I18n translation key completion provider.
|
||||||
@ -34,37 +28,45 @@ public class KeyCompletionProvider extends CompletionProvider<CompletionParamete
|
|||||||
}
|
}
|
||||||
|
|
||||||
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
|
String previewLocale = SettingsService.getInstance(project).getState().getPreviewLocale();
|
||||||
|
String prefix = SettingsService.getInstance(project).getState().getPrefix();
|
||||||
|
|
||||||
String query = result.getPrefixMatcher().getPrefix();
|
String path = result.getPrefixMatcher().getPrefix();
|
||||||
List<String> sections = TranslationsUtil.getSections(query);
|
|
||||||
String lastSection = null;
|
|
||||||
|
|
||||||
if(!sections.isEmpty() && !query.endsWith(".")) {
|
DataStore instance = DataStore.getInstance(project);
|
||||||
lastSection = sections.remove(sections.size() - 1);
|
Map<String, String> map = new HashMap<>();
|
||||||
|
collect(map, instance.getTranslations().getNodes(), null, previewLocale, prefix);
|
||||||
|
Map<String, String> containedPath = new HashMap<>();
|
||||||
|
StringBuilder prefixedKey = new StringBuilder();
|
||||||
|
int maxPrefixLookUpLength = 5;
|
||||||
|
while (containedPath.isEmpty() && maxPrefixLookUpLength-- > 0) {
|
||||||
|
for (Map.Entry<String, String> e : map.entrySet()) {
|
||||||
|
if (e.getKey().startsWith(path)) {
|
||||||
|
containedPath.put(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (path.isEmpty()) break;
|
||||||
|
if (containedPath.isEmpty()) {
|
||||||
|
prefixedKey.append(path.charAt(0));
|
||||||
|
}
|
||||||
|
path = path.substring(1);
|
||||||
}
|
}
|
||||||
|
containedPath.forEach((key, value) -> {
|
||||||
|
result.addElement(LookupElementBuilder.create(prefixedKey + key).appendTailText(" I18n("+previewLocale+": "+value+")", true));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
String path = TranslationsUtil.sectionsToFullPath(sections);
|
private void collect(Map<String, String> map, LocalizedNode node, String path, String locale, String prefix) {
|
||||||
|
if (node.isLeaf() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) {
|
||||||
LocalizedNode node = sections.isEmpty() ? DataStore.getInstance(project).getTranslations().getNodes()
|
String value = node.getValue().get(locale);
|
||||||
: DataStore.getInstance(project).getTranslations().getNode(path);
|
map.put(path, value);
|
||||||
|
if (prefix != null && !prefix.isEmpty()) {
|
||||||
if(node == null) { // Unknown translation
|
map.put(prefix + "." + path, value);
|
||||||
return;
|
}
|
||||||
}
|
} else {
|
||||||
|
for (LocalizedNode child : node.getChildren()) {
|
||||||
for(LocalizedNode children : node.getChildren()) {
|
collect(map, child, path == null || path.isEmpty() ? child.getKey() : path + "." + child.getKey(), locale, prefix);
|
||||||
if(lastSection == null || children.getKey().startsWith(lastSection)) {
|
|
||||||
// Construct full key path / Fore nested objects add '.' to indicate deeper level
|
|
||||||
String fullKey = (path.isEmpty() ? children.getKey() : path + "." + children.getKey()) + (children.isLeaf() ? "" : ".");
|
|
||||||
|
|
||||||
result.addElement(LookupElementBuilder.create(fullKey)
|
|
||||||
.appendTailText(getTailText(children, previewLocale), true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTailText(LocalizedNode node, String previewLocale) {
|
|
||||||
return !node.isLeaf() ? " I18n([])"
|
|
||||||
: " I18n(" + previewLocale + ": " + node.getValue().get(previewLocale) + ")";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@ package de.marhali.easyi18n.editor.generic;
|
|||||||
|
|
||||||
import com.intellij.codeInsight.completion.CompletionContributor;
|
import com.intellij.codeInsight.completion.CompletionContributor;
|
||||||
import com.intellij.codeInsight.completion.CompletionType;
|
import com.intellij.codeInsight.completion.CompletionType;
|
||||||
|
import com.intellij.lang.*;
|
||||||
import com.intellij.patterns.*;
|
import com.intellij.patterns.*;
|
||||||
import com.intellij.psi.PsiLiteralValue;
|
import com.intellij.psi.*;
|
||||||
|
import com.intellij.psi.xml.*;
|
||||||
import de.marhali.easyi18n.editor.KeyCompletionProvider;
|
import de.marhali.easyi18n.editor.KeyCompletionProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -13,6 +15,10 @@ import de.marhali.easyi18n.editor.KeyCompletionProvider;
|
|||||||
public class GenericKeyCompletionContributor extends CompletionContributor {
|
public class GenericKeyCompletionContributor extends CompletionContributor {
|
||||||
|
|
||||||
public GenericKeyCompletionContributor() {
|
public GenericKeyCompletionContributor() {
|
||||||
|
extend(CompletionType.BASIC, PlatformPatterns.psiElement(PlainTextTokenTypes.PLAIN_TEXT),
|
||||||
|
new KeyCompletionProvider());
|
||||||
|
extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(XmlElement.class),
|
||||||
|
new KeyCompletionProvider());
|
||||||
extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralValue.class),
|
extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralValue.class),
|
||||||
new KeyCompletionProvider());
|
new KeyCompletionProvider());
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
package de.marhali.easyi18n.io.implementation;
|
||||||
|
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.intellij.openapi.application.*;
|
||||||
|
import com.intellij.openapi.project.*;
|
||||||
|
import com.intellij.openapi.vfs.*;
|
||||||
|
import de.marhali.easyi18n.io.*;
|
||||||
|
import de.marhali.easyi18n.model.*;
|
||||||
|
import de.marhali.easyi18n.util.*;
|
||||||
|
import org.jetbrains.annotations.*;
|
||||||
|
import thito.nodeflow.config.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.*;
|
||||||
|
|
||||||
|
public class YamlTranslatorIO implements TranslatorIO {
|
||||||
|
@Override
|
||||||
|
public void read(@NotNull Project project, @NotNull String directoryPath, @NotNull Consumer<Translations> callback) {
|
||||||
|
ApplicationManager.getApplication().saveAll(); // Save opened files (required if new locales were added)
|
||||||
|
|
||||||
|
ApplicationManager.getApplication().runReadAction(() -> {
|
||||||
|
VirtualFile directory = LocalFileSystem.getInstance().findFileByIoFile(new File(directoryPath));
|
||||||
|
|
||||||
|
if(directory == null || directory.getChildren() == null) {
|
||||||
|
throw new IllegalArgumentException("Specified folder is invalid (" + directoryPath + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile[] files = directory.getChildren();
|
||||||
|
|
||||||
|
List<String> locales = new ArrayList<>();
|
||||||
|
LocalizedNode nodes = new LocalizedNode(LocalizedNode.ROOT_KEY, new ArrayList<>());
|
||||||
|
|
||||||
|
try {
|
||||||
|
for(VirtualFile file : files) {
|
||||||
|
|
||||||
|
if(!IOUtil.isFileRelevant(project, file)) { // File does not matches pattern
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
locales.add(file.getNameWithoutExtension());
|
||||||
|
|
||||||
|
try (Reader reader = new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8)) {
|
||||||
|
Section section = Section.parseToMap(reader);
|
||||||
|
load(file.getNameWithoutExtension(), nodes, section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.accept(new Translations(locales, nodes));
|
||||||
|
|
||||||
|
} catch(IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
callback.accept(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void load(String locale, LocalizedNode node, Section section) {
|
||||||
|
if (section instanceof MapSection) {
|
||||||
|
for (String key : section.getKeys()) {
|
||||||
|
LocalizedNode child = node.getChildren(key);
|
||||||
|
if (child == null) {
|
||||||
|
node.addChildren(child = new LocalizedNode(key, new ArrayList<>()));
|
||||||
|
}
|
||||||
|
LocalizedNode finalChild = child;
|
||||||
|
MapSection map = section.getMap(key).orElse(null);
|
||||||
|
if (map != null) {
|
||||||
|
load(locale, finalChild, map);
|
||||||
|
} else {
|
||||||
|
String value = section.getString(key).orElse(null);
|
||||||
|
if (value != null) {
|
||||||
|
child.getValue().put(locale, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void save(LocalizedNode node, String locale, Section section, String path) {
|
||||||
|
if (node.isLeaf() && !node.getKey().equals(LocalizedNode.ROOT_KEY)) {
|
||||||
|
String value = node.getValue().get(locale);
|
||||||
|
if (value != null) {
|
||||||
|
section.set(path, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (LocalizedNode child : node.getChildren()) {
|
||||||
|
save(child, locale, section, path == null ? child.getKey() : path + "." + child.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(@NotNull Project project, @NotNull Translations translations, @NotNull String directoryPath, @NotNull Consumer<Boolean> callback) {
|
||||||
|
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||||
|
try {
|
||||||
|
for(String locale : translations.getLocales()) {
|
||||||
|
Section section = new MapSection();
|
||||||
|
|
||||||
|
save(translations.getNodes(), locale, section, null);
|
||||||
|
|
||||||
|
String fullPath = directoryPath + "/" + locale + ".yml";
|
||||||
|
VirtualFile file = LocalFileSystem.getInstance().findFileByIoFile(new File(fullPath));
|
||||||
|
|
||||||
|
file.setBinaryContent(Section.toString(section).getBytes(file.getCharset()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Successfully saved
|
||||||
|
callback.accept(true);
|
||||||
|
|
||||||
|
} catch(IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
callback.accept(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ public class SettingsState {
|
|||||||
private String localesPath;
|
private String localesPath;
|
||||||
private String filePattern;
|
private String filePattern;
|
||||||
private String previewLocale;
|
private String previewLocale;
|
||||||
|
private String prefix;
|
||||||
private Boolean codeAssistance;
|
private Boolean codeAssistance;
|
||||||
|
|
||||||
public SettingsState() {}
|
public SettingsState() {}
|
||||||
@ -51,4 +52,12 @@ public class SettingsState {
|
|||||||
public void setCodeAssistance(boolean codeAssistance) {
|
public void setCodeAssistance(boolean codeAssistance) {
|
||||||
this.codeAssistance = codeAssistance;
|
this.codeAssistance = codeAssistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPrefix(String prefix) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrefix() {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,9 +3,7 @@ package de.marhali.easyi18n.util;
|
|||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project;
|
||||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile;
|
||||||
import de.marhali.easyi18n.io.implementation.JsonTranslatorIO;
|
import de.marhali.easyi18n.io.implementation.*;
|
||||||
import de.marhali.easyi18n.io.implementation.ModularizedJsonTranslatorIO;
|
|
||||||
import de.marhali.easyi18n.io.implementation.PropertiesTranslatorIO;
|
|
||||||
import de.marhali.easyi18n.io.TranslatorIO;
|
import de.marhali.easyi18n.io.TranslatorIO;
|
||||||
|
|
||||||
import de.marhali.easyi18n.service.SettingsService;
|
import de.marhali.easyi18n.service.SettingsService;
|
||||||
@ -50,7 +48,8 @@ public class IOUtil {
|
|||||||
|
|
||||||
case "properties":
|
case "properties":
|
||||||
return new PropertiesTranslatorIO();
|
return new PropertiesTranslatorIO();
|
||||||
|
case "yml":
|
||||||
|
return new YamlTranslatorIO();
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("Unsupported i18n locale file format: " +
|
throw new UnsupportedOperationException("Unsupported i18n locale file format: " +
|
||||||
any.get().getFileType().getDefaultExtension());
|
any.get().getFileType().getDefaultExtension());
|
||||||
|
227
src/main/java/thito/nodeflow/config/ListSection.java
Normal file
227
src/main/java/thito/nodeflow/config/ListSection.java
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package thito.nodeflow.config;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.*;
|
||||||
|
|
||||||
|
public class ListSection extends ArrayList<Object> implements Section {
|
||||||
|
private Section parent;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public ListSection() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListSection(Collection<?> c) {
|
||||||
|
super();
|
||||||
|
addAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setParent(Section parent, String name) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
if (name == null && parent != null) {
|
||||||
|
if (parent instanceof ListSection) {
|
||||||
|
return String.valueOf(((ListSection) parent).indexOf(this));
|
||||||
|
}
|
||||||
|
if (parent instanceof MapSection) {
|
||||||
|
return ((MapSection) parent).entrySet().stream().filter(e -> Objects.equals(e.getValue(), this))
|
||||||
|
.findAny().map(Map.Entry::getKey).orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Object> getObject(int index) {
|
||||||
|
return index >= 0 && index < size() ? Optional.ofNullable(get(index)) : Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Section getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getKeys() {
|
||||||
|
return IntStream.range(0, size()).mapToObj(String::valueOf).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<?> getInScope(String key) {
|
||||||
|
try {
|
||||||
|
return Optional.ofNullable(get(Integer.parseInt(key)));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInScope(String key, Object value) {
|
||||||
|
try {
|
||||||
|
set(Integer.parseInt(key), value);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object set(int index, Object element) {
|
||||||
|
element = Section.wrap(element);
|
||||||
|
if (element instanceof Section) element = Section.wrapParent(this, null, (Section) element);
|
||||||
|
return super.set(index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(Object o) {
|
||||||
|
o = Section.wrap(o);
|
||||||
|
if (o instanceof Section) o = Section.wrapParent(this, null, (Section) o);
|
||||||
|
return super.add(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, Object element) {
|
||||||
|
element = Section.wrap(element);
|
||||||
|
if (element instanceof Section) element = Section.wrapParent(this, null, (Section) element);
|
||||||
|
super.add(index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<?> c) {
|
||||||
|
c.forEach(o -> add(o));
|
||||||
|
return !c.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int index, Collection<?> c) {
|
||||||
|
List<Object> wrapped = new ArrayList<>();
|
||||||
|
c.forEach(obj -> {
|
||||||
|
Object o = Section.wrap(obj);
|
||||||
|
if (o instanceof Section) o = Section.wrapParent(this, null, (Section) o);
|
||||||
|
wrapped.add(o);
|
||||||
|
});
|
||||||
|
return super.addAll(index, wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Section.toString(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Enum<T>> Optional<T> getEnum(int index, Class<T> clz) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
try {
|
||||||
|
return Enum.valueOf(clz, String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getString(int index) {
|
||||||
|
return getObject(index).map(String::valueOf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Integer> getInteger(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
try {
|
||||||
|
return Integer.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Double> getDouble(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
try {
|
||||||
|
return Double.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Long> getLong(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
try {
|
||||||
|
return Long.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Float> getFloat(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
try {
|
||||||
|
return Float.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Short> getShort(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
try {
|
||||||
|
return Short.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Byte> getByte(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
try {
|
||||||
|
return Byte.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Character> getCharacter(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
String text = String.valueOf(o);
|
||||||
|
return text.isEmpty() ? null : text.charAt(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Boolean> getBoolean(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
String text = String.valueOf(o);
|
||||||
|
return text.equals("true") ? Boolean.TRUE : text.equals("false") ? Boolean.FALSE : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<MapSection> getMap(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
if (o instanceof Map) {
|
||||||
|
if (o instanceof MapSection) return (MapSection) o;
|
||||||
|
MapSection mapSection = new MapSection((Map<?, ?>) o);
|
||||||
|
mapSection.setParent(this, null);
|
||||||
|
return mapSection;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ListSection> getList(int index) {
|
||||||
|
return getObject(index).map(o -> {
|
||||||
|
if (o instanceof List) {
|
||||||
|
if (o instanceof ListSection) {
|
||||||
|
return (ListSection) o;
|
||||||
|
}
|
||||||
|
ListSection list = new ListSection((List<?>) o);
|
||||||
|
list.setParent(this, null);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
ListSection list = new ListSection(Collections.singleton(o));
|
||||||
|
list.setParent(this, null);
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
73
src/main/java/thito/nodeflow/config/MapSection.java
Normal file
73
src/main/java/thito/nodeflow/config/MapSection.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package thito.nodeflow.config;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class MapSection extends HashMap<String, Object> implements Section {
|
||||||
|
private Section parent;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public MapSection() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MapSection(Map<?, ?> m) {
|
||||||
|
super();
|
||||||
|
m.forEach((key, value) -> put(String.valueOf(key), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setParent(Section parent, String name) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
if (name == null && parent != null) {
|
||||||
|
if (parent instanceof ListSection) {
|
||||||
|
return String.valueOf(((ListSection) parent).indexOf(this));
|
||||||
|
}
|
||||||
|
if (parent instanceof MapSection) {
|
||||||
|
return ((MapSection) parent).entrySet().stream().filter(e -> Objects.equals(e.getValue(), this))
|
||||||
|
.findAny().map(Entry::getKey).orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Section getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInScope(String key, Object value) {
|
||||||
|
put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object put(String key, Object value) {
|
||||||
|
value = Section.wrap(value);
|
||||||
|
if (value instanceof Section) value = Section.wrapParent(this, key, (Section) value);
|
||||||
|
return super.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putAll(Map<? extends String, ?> m) {
|
||||||
|
m.forEach(this::put);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getKeys() {
|
||||||
|
return keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<?> getInScope(String key) {
|
||||||
|
return Optional.ofNullable(get(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Section.toString(this);
|
||||||
|
}
|
||||||
|
}
|
215
src/main/java/thito/nodeflow/config/Section.java
Normal file
215
src/main/java/thito/nodeflow/config/Section.java
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
package thito.nodeflow.config;
|
||||||
|
|
||||||
|
import org.yaml.snakeyaml.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.regex.*;
|
||||||
|
|
||||||
|
public interface Section {
|
||||||
|
String SEPARATOR = ".";
|
||||||
|
static Object wrap(Object o) {
|
||||||
|
if (o instanceof Section) return o;
|
||||||
|
if (o instanceof List) {
|
||||||
|
return new ListSection((List<?>) o);
|
||||||
|
}
|
||||||
|
if (o instanceof Map) {
|
||||||
|
return new MapSection((Map<?, ?>) o);
|
||||||
|
}
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
static String getName(String path) {
|
||||||
|
String[] split = path.split(Pattern.quote(SEPARATOR));
|
||||||
|
return split[split.length - 1];
|
||||||
|
}
|
||||||
|
static Section wrapParent(Section parent, String name, Section current) {
|
||||||
|
if (current.getParent() != null && current.getParent() != parent) {
|
||||||
|
if (current instanceof MapSection) {
|
||||||
|
MapSection mapSection = new MapSection((MapSection) current);
|
||||||
|
mapSection.setParent(parent, name);
|
||||||
|
return mapSection;
|
||||||
|
}
|
||||||
|
if (current instanceof ListSection) {
|
||||||
|
ListSection objects = new ListSection((ListSection) current);
|
||||||
|
objects.setParent(parent, name);
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (current instanceof MapSection) {
|
||||||
|
((MapSection) current).setParent(parent, name);
|
||||||
|
}
|
||||||
|
if (current instanceof ListSection) {
|
||||||
|
((ListSection) current).setParent(parent, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
static String toString(Section section) {
|
||||||
|
DumperOptions options = new DumperOptions();
|
||||||
|
options.setIndent(4);
|
||||||
|
options.setAllowUnicode(true);
|
||||||
|
options.setPrettyFlow(true);
|
||||||
|
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||||
|
Yaml yaml = new Yaml(options);
|
||||||
|
return yaml.dumpAsMap(section);
|
||||||
|
}
|
||||||
|
static MapSection parseToMap(Reader reader) {
|
||||||
|
Yaml yaml = new Yaml();
|
||||||
|
return new MapSection(yaml.loadAs(reader, Map.class));
|
||||||
|
}
|
||||||
|
Set<String> getKeys();
|
||||||
|
default Set<String> getPaths() {
|
||||||
|
Set<String> paths = new HashSet<>();
|
||||||
|
for (String k : getKeys()) {
|
||||||
|
Object lookup = getInScope(k).orElse(null);
|
||||||
|
if (lookup instanceof Section) {
|
||||||
|
for (String p : ((Section) lookup).getPaths()) {
|
||||||
|
paths.add(k + "." + p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
String getName();
|
||||||
|
default String getPath() {
|
||||||
|
StringBuilder path = new StringBuilder(getName());
|
||||||
|
Section parent;
|
||||||
|
while ((parent = getParent()) != null) {
|
||||||
|
path.insert(0, parent.getName() + SEPARATOR);
|
||||||
|
}
|
||||||
|
return path.toString();
|
||||||
|
}
|
||||||
|
Section getParent();
|
||||||
|
Optional<?> getInScope(String key);
|
||||||
|
void setInScope(String key, Object value);
|
||||||
|
default void set(String path, Object value) {
|
||||||
|
String[] paths = path.split(Pattern.quote(SEPARATOR));
|
||||||
|
Object lookup = this;
|
||||||
|
for (int i = 0; i < paths.length - 1; i++) {
|
||||||
|
Section oldLookup = (Section) lookup;
|
||||||
|
lookup = oldLookup.getInScope(paths[i]).orElse(null);
|
||||||
|
if (!(lookup instanceof Section)) {
|
||||||
|
oldLookup.setInScope(paths[i], lookup = new MapSection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (paths.length > 0) {
|
||||||
|
((Section) lookup).setInScope(paths[paths.length - 1], value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default Optional<?> getObject(String path) {
|
||||||
|
String[] paths = path.split(Pattern.quote(SEPARATOR));
|
||||||
|
Object lookup = this;
|
||||||
|
for (String s : paths) {
|
||||||
|
if (lookup instanceof Section) {
|
||||||
|
lookup = ((Section) lookup).getInScope(s).orElse(null);
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(lookup);
|
||||||
|
}
|
||||||
|
default <T extends Enum<T>> Optional<T> getEnum(String path, Class<T> clz) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
try {
|
||||||
|
return Enum.valueOf(clz, String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<String> getString(String path) {
|
||||||
|
return getObject(path).map(String::valueOf);
|
||||||
|
}
|
||||||
|
default Optional<Integer> getInteger(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
try {
|
||||||
|
return Integer.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<Double> getDouble(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
try {
|
||||||
|
return Double.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<Long> getLong(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
try {
|
||||||
|
return Long.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<Float> getFloat(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
try {
|
||||||
|
return Float.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<Short> getShort(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
try {
|
||||||
|
return Short.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<Byte> getByte(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
try {
|
||||||
|
return Byte.valueOf(String.valueOf(o));
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<Character> getCharacter(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
String text = String.valueOf(o);
|
||||||
|
return text.isEmpty() ? null : text.charAt(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<Boolean> getBoolean(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
String text = String.valueOf(o);
|
||||||
|
return text.equals("true") ? Boolean.TRUE : text.equals("false") ? Boolean.FALSE : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<MapSection> getMap(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
if (o instanceof Map) {
|
||||||
|
if (o instanceof MapSection) return (MapSection) o;
|
||||||
|
MapSection mapSection = new MapSection((Map<?, ?>) o);
|
||||||
|
mapSection.setParent(this, Section.getName(path));
|
||||||
|
return mapSection;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
default Optional<ListSection> getList(String path) {
|
||||||
|
return getObject(path).map(o -> {
|
||||||
|
if (o instanceof List) {
|
||||||
|
if (o instanceof ListSection) {
|
||||||
|
return (ListSection) o;
|
||||||
|
}
|
||||||
|
ListSection list = new ListSection((List<?>) o);
|
||||||
|
list.setParent(this, Section.getName(path));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
ListSection list = new ListSection(Collections.singleton(o));
|
||||||
|
list.setParent(this, Section.getName(path));
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -16,3 +16,4 @@ settings.path.text=Locales directory
|
|||||||
settings.path.file-pattern=Translation file pattern
|
settings.path.file-pattern=Translation file pattern
|
||||||
settings.preview=Preview locale
|
settings.preview=Preview locale
|
||||||
settings.editor.assistance=I18n key completion and annotation inside editor
|
settings.editor.assistance=I18n key completion and annotation inside editor
|
||||||
|
settings.path.prefix=Prefix
|
Loading…
x
Reference in New Issue
Block a user