Yaml<->Props conversion commands, tests, integration in VSCode, Eclipse
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018, 2023 Pivotal, Inc.
|
||||
* Copyright (c) 2018, 2024 Pivotal, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
@@ -129,7 +129,7 @@ public class BootLanguageServerInitializer implements InitializingBean {
|
||||
builder.add(c);
|
||||
}
|
||||
|
||||
components = builder.build(server);
|
||||
components = builder.build(appContext);
|
||||
|
||||
final SimpleTextDocumentService documents = server.getTextDocumentService();
|
||||
|
||||
|
||||
@@ -105,7 +105,12 @@ public class BootPropertiesLanguageServerComponents implements LanguageServerCom
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Register YAML -> Props conversion command
|
||||
new YamlToPropertiesCommand(server);
|
||||
|
||||
// Register Props -> YAML conversion command
|
||||
new PropertiesToYamlCommand(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.properties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.LazyTextDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
|
||||
public class PropertiesToYamlCommand {
|
||||
|
||||
static final String CMD_PROPS_TO_YAML = "sts/boot/props-to-yaml";
|
||||
|
||||
private static final Pattern COMMENT = Pattern.compile("(?m)^\\s*(\\#|\\!)");
|
||||
|
||||
private final SimpleLanguageServer server;
|
||||
|
||||
PropertiesToYamlCommand(SimpleLanguageServer server) {
|
||||
this.server = server;
|
||||
server.onCommand(CMD_PROPS_TO_YAML, params -> execute(params.getArguments()));
|
||||
}
|
||||
|
||||
private CompletableFuture<ShowDocumentResult> execute(List<Object> arguments) {
|
||||
String propsUri = arguments.get(0) instanceof JsonElement ? ((JsonElement) arguments.get(0)).getAsString() : (String) arguments.get(0);
|
||||
String yamlUri = arguments.get(1) instanceof JsonElement ? ((JsonElement) arguments.get(1)).getAsString() : (String) arguments.get(1);
|
||||
Boolean replace = arguments.get(2) instanceof JsonElement ? ((JsonElement) arguments.get(2)).getAsBoolean() : (Boolean) arguments.get(2);
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return createWorkspaceEdit(propsUri, yamlUri, replace);
|
||||
} catch (IOException | BadLocationException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
})
|
||||
.thenCompose(we -> server.getClient().applyEdit(new ApplyWorkspaceEditParams(we, "Convert .properties to .yaml")))
|
||||
.thenCompose(res -> res.isApplied() ? server.getClient().showDocument(new ShowDocumentParams(yamlUri)) : CompletableFuture.completedFuture(new ShowDocumentResult(false)));
|
||||
}
|
||||
|
||||
private WorkspaceEdit createWorkspaceEdit(String propsUri, String yamlUri, Boolean replace) throws IOException, BadLocationException {
|
||||
Path yamlFile = Paths.get(URI.create(yamlUri));
|
||||
if (Files.exists(yamlFile)) {
|
||||
throw new IOException("File %s already exists!".formatted(yamlFile.toString()));
|
||||
}
|
||||
|
||||
TextDocument doc = getDocument(propsUri, LanguageId.BOOT_PROPERTIES);
|
||||
String propsContent = doc.get();
|
||||
List<String> errors = new ArrayList<>();
|
||||
List<String> warnings = new ArrayList<>();
|
||||
|
||||
if (hasComments(propsContent)) {
|
||||
warnings.add("The yaml file had comments which are lost in the refactoring!");
|
||||
}
|
||||
|
||||
Multimap<String, String> properties = load(new StringReader(propsContent));
|
||||
PropertiesToYamlConverter converter = new PropertiesToYamlConverter(properties);
|
||||
|
||||
StringBuilder yamlContent = new StringBuilder();
|
||||
errors.addAll(converter.getErrors());
|
||||
warnings.addAll(converter.getWarnings());
|
||||
YamlToPropertiesCommand.addReportHeaderComment(yamlContent, errors, warnings);
|
||||
yamlContent.append(converter.getYaml());
|
||||
|
||||
return replace ? YamlToPropertiesCommand.createReplaceFileWorkspaceEdit(propsUri, yamlUri, doc, yamlContent.toString()) : YamlToPropertiesCommand.createNewFileWorkspaceEdit(yamlUri, yamlContent.toString());
|
||||
}
|
||||
|
||||
private Multimap<String, String> load(Reader content) throws IOException {
|
||||
Multimap<String, String> map = Multimaps.newMultimap(
|
||||
new LinkedTreeMap<String, Collection<String>>(),
|
||||
LinkedHashSet::new
|
||||
);
|
||||
Properties loader = new Properties() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public synchronized Object put(Object key, Object value) {
|
||||
map.put((String)key, (String)value);
|
||||
return super.put(key, value);
|
||||
}
|
||||
};
|
||||
loader.load(content);
|
||||
return map;
|
||||
}
|
||||
|
||||
private static boolean hasComments(String propsContent) {
|
||||
return COMMENT.matcher(propsContent).find();
|
||||
}
|
||||
|
||||
private TextDocument getDocument(String uri, LanguageId language) {
|
||||
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(uri);
|
||||
return doc == null ? new LazyTextDocument(uri, language) : doc;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.properties;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.core.runtime.Assert;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPath;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment;
|
||||
import org.springframework.ide.vscode.commons.yaml.path.YamlPathSegment.AtIndex;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.SafeConstructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
|
||||
/**
|
||||
* Helper class to convert (Spring Boot) .properties file content into equivalent
|
||||
* .yml file content.
|
||||
*
|
||||
* @author Kris De Volder
|
||||
*/
|
||||
class PropertiesToYamlConverter {
|
||||
|
||||
private String output;
|
||||
private ImmutableList.Builder<String> errors;
|
||||
private ImmutableList.Builder<String> warnings;
|
||||
|
||||
class YamlBuilder {
|
||||
final YamlPath path;
|
||||
final List<Object> scalars = new ArrayList<>();
|
||||
final LinkedTreeMap<Integer, YamlBuilder> listItems = new LinkedTreeMap<>();
|
||||
final LinkedTreeMap<String, YamlBuilder> mapEntries = new LinkedTreeMap<>();
|
||||
|
||||
public YamlBuilder(YamlPath path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
void addProperty(YamlPath path, String value) {
|
||||
if (path.isEmpty()) {
|
||||
scalars.add(objectify(value));
|
||||
} else {
|
||||
YamlPathSegment segment = path.getSegment(0);
|
||||
YamlBuilder subBuilder;
|
||||
if (segment instanceof AtIndex) {
|
||||
subBuilder = getSubBuilder(listItems, segment, segment.toIndex());
|
||||
} else {
|
||||
subBuilder = getSubBuilder(mapEntries, segment, segment.toPropString());
|
||||
}
|
||||
subBuilder.addProperty(path.dropFirst(1), value);
|
||||
}
|
||||
}
|
||||
|
||||
private Object objectify(String value) {
|
||||
if (value != null) {
|
||||
Object parsed = null;
|
||||
try {
|
||||
parsed = new BigInteger(value);
|
||||
} catch (NumberFormatException e) {
|
||||
try {
|
||||
parsed = new BigDecimal(value);
|
||||
} catch (NumberFormatException e2) {
|
||||
if (value.equals("true")) {
|
||||
parsed = true;
|
||||
} else if (value.equals("false")) {
|
||||
parsed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parsed!=null && parsed.toString().equals(value)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private <T> YamlBuilder getSubBuilder(LinkedTreeMap<T, YamlBuilder> subBuilders, YamlPathSegment segment, T key) {
|
||||
YamlBuilder existing = subBuilders.get(key);
|
||||
if (existing==null) {
|
||||
subBuilders.put(key, existing = new YamlBuilder(path.append(segment)));
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
|
||||
public Object build() {
|
||||
if (!scalars.isEmpty()) {
|
||||
if (listItems.isEmpty() && mapEntries.isEmpty()) {
|
||||
if (scalars.size()>1) {
|
||||
warnings.add("Multiple values "+ scalars +" assigned to '"+path.toPropString()+"'. Values are merged into a yaml sequence node.");
|
||||
return scalars;
|
||||
} else {
|
||||
return scalars.get(0);
|
||||
}
|
||||
} else {
|
||||
if (!mapEntries.isEmpty()) {
|
||||
errors.add(
|
||||
"Direct assignment '"+path.toPropString()+"="+scalars.get(0)+"' can not be combined " +
|
||||
"with sub-property assignment '"+path.toPropString()+"." + mapEntries.keySet().iterator().next()+"...'. "+
|
||||
"Direct assignment is dropped!"
|
||||
);
|
||||
} else {
|
||||
errors.add(
|
||||
"Direct assignment '"+path.toPropString()+"="+scalars.get(0)+"' can not be combined " +
|
||||
"with sequence assignment '"+path.toPropString()+"[" + listItems.keySet().iterator().next()+"]...'. "+
|
||||
"Direct assignments are dropped!"
|
||||
);
|
||||
}
|
||||
scalars.clear();
|
||||
}
|
||||
}
|
||||
Assert.isLegal(scalars.isEmpty());
|
||||
if (!listItems.isEmpty() && !mapEntries.isEmpty()) {
|
||||
warnings.add("'"+path.toPropString()+"' has some entries that look like list items and others that look like map entries. "
|
||||
+ "All these entries are treated as map entries!");
|
||||
for (Entry<Integer, YamlBuilder> listItem : listItems.entrySet()) {
|
||||
mapEntries.put(listItem.getKey().toString(), listItem.getValue());
|
||||
}
|
||||
listItems.clear();
|
||||
}
|
||||
if (!listItems.isEmpty()) {
|
||||
return listItems.values().stream()
|
||||
.map(childBuilder -> childBuilder.build())
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
LinkedTreeMap<String, Object> map = new LinkedTreeMap<>();
|
||||
for (Entry<String, YamlBuilder> entry : mapEntries.entrySet()) {
|
||||
map.put(entry.getKey(), entry.getValue().build());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PropertiesToYamlConverter(Multimap<String, String> properties) {
|
||||
this.errors = ImmutableList.builder();
|
||||
this.warnings = ImmutableList.builder();
|
||||
if (properties.isEmpty()) {
|
||||
output = "";
|
||||
return;
|
||||
}
|
||||
YamlBuilder root = new YamlBuilder(YamlPath.EMPTY);
|
||||
for (Entry<String, String> e : properties.entries()) {
|
||||
root.addProperty(YamlPath.fromProperty(e.getKey()), e.getValue());
|
||||
}
|
||||
Object object = root.build();
|
||||
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
options.setPrettyFlow(true);
|
||||
|
||||
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(options), options);
|
||||
this.output = yaml.dump(object);
|
||||
}
|
||||
|
||||
public List<String> getErrors() {
|
||||
return errors.build();
|
||||
}
|
||||
|
||||
public List<String> getWarnings() {
|
||||
return warnings.build();
|
||||
}
|
||||
|
||||
public String getYaml() {
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.properties;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
import org.eclipse.lsp4j.AnnotatedTextEdit;
|
||||
import org.eclipse.lsp4j.ApplyWorkspaceEditParams;
|
||||
import org.eclipse.lsp4j.ChangeAnnotation;
|
||||
import org.eclipse.lsp4j.CreateFile;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.RenameFile;
|
||||
import org.eclipse.lsp4j.ShowDocumentParams;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.TextDocumentEdit;
|
||||
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
|
||||
import org.eclipse.lsp4j.WorkspaceEdit;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.LazyTextDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.comments.CommentType;
|
||||
import org.yaml.snakeyaml.events.CommentEvent;
|
||||
import org.yaml.snakeyaml.events.Event;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
class YamlToPropertiesCommand {
|
||||
|
||||
private static final String LABEL = "Convert .yaml to .properties";
|
||||
|
||||
static final String CMD_YAML_TO_PROPS = "sts/boot/yaml-to-props";
|
||||
|
||||
private final SimpleLanguageServer server;
|
||||
|
||||
YamlToPropertiesCommand(SimpleLanguageServer server) {
|
||||
this.server = server;
|
||||
server.onCommand(CMD_YAML_TO_PROPS, params -> execute(params.getArguments()));
|
||||
}
|
||||
|
||||
private CompletableFuture<ShowDocumentResult> execute(List<Object> arguments) {
|
||||
String yamlUri = arguments.get(0) instanceof JsonElement ? ((JsonElement) arguments.get(0)).getAsString() : (String) arguments.get(0);
|
||||
String propsUri = arguments.get(1) instanceof JsonElement ? ((JsonElement) arguments.get(1)).getAsString() : (String) arguments.get(1);
|
||||
Boolean replace = arguments.get(2) instanceof JsonElement ? ((JsonElement) arguments.get(2)).getAsBoolean() : (Boolean) arguments.get(2);
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
return createWorkspaceEdit(yamlUri, propsUri, replace);
|
||||
} catch (IOException | BadLocationException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
})
|
||||
.thenCompose(we -> server.getClient().applyEdit(new ApplyWorkspaceEditParams(we, LABEL)))
|
||||
.thenCompose(res -> res.isApplied() ? server.getClient().showDocument(new ShowDocumentParams(propsUri)) : CompletableFuture.completedFuture(new ShowDocumentResult(false)));
|
||||
}
|
||||
|
||||
private WorkspaceEdit createWorkspaceEdit(String yamlUri, String propsUri, boolean replace) throws IOException, BadLocationException {
|
||||
Path propsFile = Paths.get(URI.create(propsUri));
|
||||
if (Files.exists(propsFile)) {
|
||||
throw new IOException("File %s already exists!".formatted(propsFile.toString()));
|
||||
}
|
||||
|
||||
List<String> errors = new ArrayList<>();
|
||||
List<String> warnings = new ArrayList<>();
|
||||
TextDocument doc = getDocument(server, yamlUri, LanguageId.BOOT_PROPERTIES_YAML);
|
||||
String yamlContent = doc.get();
|
||||
|
||||
if (hasComments(yamlContent)) {
|
||||
warnings.add("The yaml file had comments which are lost in the refactoring!");
|
||||
}
|
||||
|
||||
StringBuilder propsContent = new StringBuilder();
|
||||
for (Object d : new Yaml().loadAll(new StringReader(yamlContent))) {
|
||||
if (d instanceof Map) {
|
||||
// Add doc divider if not empty
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, ?> o = (Map<String, ?>) d;
|
||||
YamlToPropertiesConverter converter = new YamlToPropertiesConverter(o);
|
||||
Properties props = converter.getProperties();
|
||||
StringWriter write = new StringWriter();
|
||||
props.store(write, null);
|
||||
if (!propsContent.isEmpty()) {
|
||||
propsContent.append("#---\n");
|
||||
} else {
|
||||
addReportHeaderComment(propsContent, errors, warnings);
|
||||
}
|
||||
// Skip over the date header. Comments are not present but date header is.
|
||||
if (write.getBuffer().charAt(0) == '#') {
|
||||
int idx = write.getBuffer().indexOf("\n");
|
||||
propsContent.append(idx >= 0 && idx < write.getBuffer().length() ? write.getBuffer().substring(idx + 1) : write.getBuffer().toString());
|
||||
} else {
|
||||
propsContent.append(write.getBuffer().toString());
|
||||
}
|
||||
} else if (d == null) {
|
||||
if (!propsContent.isEmpty()) {
|
||||
propsContent.append("#---\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return replace ? createReplaceFileWorkspaceEdit(yamlUri, propsUri, doc, propsContent.toString()) : createNewFileWorkspaceEdit(propsUri, propsContent.toString());
|
||||
}
|
||||
|
||||
|
||||
private static boolean hasComments(String yamlContent) {
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setProcessComments(true);
|
||||
for (Event e : new Yaml(loaderOptions).parse(new StringReader(yamlContent))) {
|
||||
if (e instanceof CommentEvent ce) {
|
||||
if (ce.getCommentType() == CommentType.BLANK_LINE) {
|
||||
// document separator
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static WorkspaceEdit createReplaceFileWorkspaceEdit(String sourceUri, String targetUri, TextDocument oldDoc, String newContent) throws BadLocationException {
|
||||
String changeAnnotationId = UUID.randomUUID().toString();
|
||||
ChangeAnnotation changeAnnotation = new ChangeAnnotation(LABEL);
|
||||
changeAnnotation.setNeedsConfirmation(true);
|
||||
|
||||
RenameFile renameFile = new RenameFile(sourceUri, targetUri);
|
||||
renameFile.setAnnotationId(changeAnnotationId);
|
||||
WorkspaceEdit we = new WorkspaceEdit(List.of(
|
||||
Either.forLeft(new TextDocumentEdit(
|
||||
new VersionedTextDocumentIdentifier(sourceUri, oldDoc.getVersion()),
|
||||
List.of(new AnnotatedTextEdit(oldDoc.toRange(0, oldDoc.getLength()), newContent, changeAnnotationId))
|
||||
)),
|
||||
Either.forRight(renameFile)
|
||||
));
|
||||
we.setChangeAnnotations(Map.of(changeAnnotationId, changeAnnotation));
|
||||
return we;
|
||||
}
|
||||
|
||||
static WorkspaceEdit createNewFileWorkspaceEdit(String uri, String content) {
|
||||
String changeAnnotationId = UUID.randomUUID().toString();
|
||||
ChangeAnnotation changeAnnotation = new ChangeAnnotation(LABEL);
|
||||
changeAnnotation.setNeedsConfirmation(false); // VSCode refactor preview errors out showing diff for newly added file
|
||||
|
||||
CreateFile createFile = new CreateFile(uri);
|
||||
createFile.setAnnotationId(changeAnnotationId);
|
||||
WorkspaceEdit we = new WorkspaceEdit(List.of(
|
||||
Either.forRight(createFile),
|
||||
Either.forLeft(new TextDocumentEdit(
|
||||
new VersionedTextDocumentIdentifier(uri, null),
|
||||
List.of(new AnnotatedTextEdit(new Range(new Position(0,0), new Position(0,0)), content, changeAnnotationId))
|
||||
))
|
||||
));
|
||||
we.setChangeAnnotations(Map.of(changeAnnotationId, changeAnnotation));
|
||||
return we;
|
||||
}
|
||||
|
||||
static TextDocument getDocument(SimpleLanguageServer server, String uri, LanguageId language) {
|
||||
TextDocument doc = server.getTextDocumentService().getLatestSnapshot(uri);
|
||||
return doc == null ? new LazyTextDocument(uri, language) : doc;
|
||||
}
|
||||
|
||||
static void addReportHeaderComment(StringBuilder content, List<String> errors, List<String> warnings) {
|
||||
if (!errors.isEmpty() || !warnings.isEmpty()) {
|
||||
content.append("# Conversion to YAML from Properties formar report\n");
|
||||
if (!errors.isEmpty()) {
|
||||
content.append("# Errors:\n");
|
||||
for (String e : errors) {
|
||||
content.append("# - %s\n".formatted(e));
|
||||
}
|
||||
}
|
||||
if (!warnings.isEmpty()) {
|
||||
content.append("# Warnings:\n");
|
||||
for (String w : warnings) {
|
||||
content.append("# - %s\n".formatted(w));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.properties;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
class YamlToPropertiesConverter {
|
||||
|
||||
private final Properties properties;
|
||||
|
||||
|
||||
public YamlToPropertiesConverter(Map<String, ?> yaml) {
|
||||
this.properties = new Properties() {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private LinkedHashMap<Object, Object> delegate = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public synchronized Object put(Object key, Object value) {
|
||||
delegate.put(key, value);
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
return delegate.entrySet();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
for (Map.Entry<String, ?> e : yaml.entrySet()) {
|
||||
readProperties(e.getValue(), e.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private void readPropertiesFromYamlMap(Map<String, ?> map, String prefix) {
|
||||
for (Map.Entry<String, ?> e : map.entrySet()) {
|
||||
readProperties(e.getValue(), "%s.%s".formatted(prefix, e.getKey()));
|
||||
}
|
||||
}
|
||||
|
||||
private void readPropertiesFromYamlList(List<?> l, String prefix) {
|
||||
for (int i = 0; i < l.size(); i++) {
|
||||
readProperties(l.get(i), "%s[%d]".formatted(prefix, i));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void readProperties(Object o, String prefix) {
|
||||
if (o instanceof Map) {
|
||||
readPropertiesFromYamlMap((Map<String, ?>) o, prefix);
|
||||
} else if ( o instanceof List) {
|
||||
readPropertiesFromYamlList((List<?>) o, prefix);
|
||||
} else {
|
||||
properties.put(prefix, o.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.properties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import({SymbolProviderTestConf.class})
|
||||
public class PropertiesToYamlCommandTest {
|
||||
|
||||
private static final String FILENAME_SUFFIX = "-conversion-test";
|
||||
|
||||
@Autowired private LanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
|
||||
private File directory;
|
||||
|
||||
@BeforeEach public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-validations/").toURI());
|
||||
|
||||
Files.walk(directory.toPath(), Integer.MAX_VALUE).filter(Files::isRegularFile).filter(p -> p.getFileName().toString().contains(FILENAME_SUFFIX)).forEach(t -> {
|
||||
try {
|
||||
Files.delete(t);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
|
||||
// trigger project creation
|
||||
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
}
|
||||
|
||||
@AfterEach public void tearDown() throws Exception {
|
||||
if (directory != null && directory.exists()) {
|
||||
Files.walk(directory.toPath(), Integer.MAX_VALUE).filter(Files::isRegularFile).filter(p -> p.getFileName().toString().contains(FILENAME_SUFFIX)).forEach(t -> {
|
||||
try {
|
||||
Files.delete(t);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void assertConversion(String propsContent, String yamlContent, boolean replace) throws Exception {
|
||||
Path yamlFilePath = directory.toPath().resolve("src/main/resources/application" + FILENAME_SUFFIX + ".yml");
|
||||
Path propsFilePath = directory.toPath().resolve("src/main/resources/application" + FILENAME_SUFFIX + ".properties");
|
||||
|
||||
if (!Files.exists(propsFilePath)) {
|
||||
Files.createDirectories(propsFilePath.getParent());
|
||||
Files.createFile(propsFilePath);
|
||||
Files.write(propsFilePath, propsContent.getBytes(), StandardOpenOption.APPEND);
|
||||
}
|
||||
|
||||
Command cmd = new Command();
|
||||
cmd.setCommand(PropertiesToYamlCommand.CMD_PROPS_TO_YAML);
|
||||
cmd.setArguments(List.of(propsFilePath.toUri().toASCIIString(), yamlFilePath.toUri().toASCIIString(), replace));
|
||||
cmd.setTitle("Convert .propeties to .yaml");
|
||||
|
||||
ShowDocumentResult res = (ShowDocumentResult) harness.perform(cmd);
|
||||
|
||||
assertThat(res.isSuccess()).isTrue();
|
||||
|
||||
assertThat(Files.exists(yamlFilePath)).isTrue();
|
||||
if (replace) {
|
||||
assertThat(Files.exists(propsFilePath)).isFalse();
|
||||
} else {
|
||||
assertThat(Files.exists(propsFilePath)).isTrue();
|
||||
}
|
||||
|
||||
assertThat(Files.readString(yamlFilePath)).isEqualTo(yamlContent);
|
||||
}
|
||||
|
||||
@Test void almostHasComments() throws Exception {
|
||||
assertConversion(
|
||||
"my.hello=Good morning!\n" +
|
||||
"my.goodbye=See ya # later\n"
|
||||
, // ==>
|
||||
"my:\n" +
|
||||
" hello: Good morning!\n" +
|
||||
" goodbye: 'See ya # later'\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Test void listItems() throws Exception {
|
||||
assertConversion(
|
||||
"some.thing[0].a=first-a\n" +
|
||||
"some.thing[0].b=first-b\n" +
|
||||
"some.thing[1].a=second-a\n" +
|
||||
"some.thing[1].b=second-b\n"
|
||||
, // ==>
|
||||
"some:\n" +
|
||||
" thing:\n" +
|
||||
" - a: first-a\n" +
|
||||
" b: first-b\n" +
|
||||
" - a: second-a\n" +
|
||||
" b: second-b\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void simple() throws Exception {
|
||||
assertConversion(
|
||||
"some.thing=vvvv\n" +
|
||||
"some.other.thing=blah\n"
|
||||
, // ==>
|
||||
"some:\n" +
|
||||
" thing: vvvv\n" +
|
||||
" other:\n" +
|
||||
" thing: blah\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void noReplacement() throws Exception {
|
||||
assertConversion(
|
||||
"some.thing=vvvv\n" +
|
||||
"some.other.thing=blah\n"
|
||||
, // ==>
|
||||
"some:\n" +
|
||||
" thing: vvvv\n" +
|
||||
" other:\n" +
|
||||
" thing: blah\n",
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@Test void nonStringyValueConversion() throws Exception {
|
||||
//See: https://www.pivotaltracker.com/story/show/154181583
|
||||
//Test that we do not add unnecessary quotes around certain types of values.
|
||||
assertConversion(
|
||||
"exponated=123.4E-12\n" +
|
||||
"server.port=8888\n" +
|
||||
"foobar.enabled=true\n" +
|
||||
"foobar.nice=false\n" +
|
||||
"fractional=0.78\n" +
|
||||
"largenumber=989898989898989898989898989898989898989898989898989898989898\n" +
|
||||
"longfractional=-0.989898989898989898989898989898989898989898989898989898989898\n"
|
||||
, // ==>
|
||||
"exponated: '123.4E-12'\n" + //quotes are added because conversion to number changes the string value
|
||||
"server:\n" +
|
||||
" port: 8888\n" +
|
||||
"foobar:\n" +
|
||||
" enabled: true\n" +
|
||||
" nice: false\n" +
|
||||
"fractional: 0.78\n" +
|
||||
"largenumber: 989898989898989898989898989898989898989898989898989898989898\n" +
|
||||
"longfractional: -0.989898989898989898989898989898989898989898989898989898989898\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void emptyFileConversion() throws Exception {
|
||||
assertConversion(
|
||||
""
|
||||
, // ==>
|
||||
"",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void multipleAssignmentProblem() throws Exception {
|
||||
assertConversion(
|
||||
"some.property=something\n" +
|
||||
"some.property=something-else"
|
||||
, // ==>
|
||||
"# Conversion to YAML from Properties formar report\n" +
|
||||
"# Warnings:\n" +
|
||||
"# - Multiple values [something, something-else] assigned to 'some.property'. Values are merged into a yaml sequence node.\n" +
|
||||
"some:\n" +
|
||||
" property:\n" +
|
||||
" - something\n" +
|
||||
" - something-else\n"
|
||||
,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void scalarAndMapConflict() throws Exception {
|
||||
assertConversion(
|
||||
"some.property=a-scalar\n" +
|
||||
"some.property.sub=sub-value"
|
||||
,
|
||||
"# Conversion to YAML from Properties formar report\n" +
|
||||
"# Errors:\n" +
|
||||
"# - Direct assignment 'some.property=a-scalar' can not be combined with sub-property assignment 'some.property.sub...'. Direct assignment is dropped!\n" +
|
||||
"some:\n" +
|
||||
" property:\n" +
|
||||
" sub: sub-value\n"
|
||||
,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void scalarAndSequenceConflict() throws Exception {
|
||||
assertConversion(
|
||||
"some.property=a-scalar\n" +
|
||||
"some.property[0]=zero\n" +
|
||||
"some.property[1]=one\n"
|
||||
,
|
||||
"# Conversion to YAML from Properties formar report\n" +
|
||||
"# Errors:\n" +
|
||||
"# - Direct assignment 'some.property=a-scalar' can not be combined with sequence assignment 'some.property[0]...'. Direct assignments are dropped!\n" +
|
||||
"some:\n" +
|
||||
" property:\n" +
|
||||
" - zero\n" +
|
||||
" - one\n"
|
||||
,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void mapAndSequenceConflict() throws Exception {
|
||||
assertConversion(
|
||||
"some.property.abc=val1\n" +
|
||||
"some.property.def=val2\n" +
|
||||
"some.property[0]=zero\n" +
|
||||
"some.property[1]=one\n"
|
||||
,
|
||||
"# Conversion to YAML from Properties formar report\n" +
|
||||
"# Warnings:\n" +
|
||||
"# - 'some.property' has some entries that look like list items and others that look like map entries. All these entries are treated as map entries!\n" +
|
||||
"some:\n" +
|
||||
" property:\n" +
|
||||
" abc: val1\n" +
|
||||
" def: val2\n" +
|
||||
" '0': zero\n" +
|
||||
" '1': one\n"
|
||||
,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void scalarAndMapAndSequenceConflict() throws Exception {
|
||||
assertConversion(
|
||||
"some.property=a-scalar\n" +
|
||||
"some.property.abc=val1\n" +
|
||||
"some.property.def=val2\n" +
|
||||
"some.property[0]=zero\n" +
|
||||
"some.property[1]=one\n"
|
||||
,
|
||||
"# Conversion to YAML from Properties formar report\n" +
|
||||
"# Errors:\n" +
|
||||
"# - Direct assignment 'some.property=a-scalar' can not be combined with sub-property assignment 'some.property.abc...'. Direct assignment is dropped!\n" +
|
||||
"# Warnings:\n" +
|
||||
"# - 'some.property' has some entries that look like list items and others that look like map entries. All these entries are treated as map entries!\n" +
|
||||
"some:\n" +
|
||||
" property:\n" +
|
||||
" abc: val1\n" +
|
||||
" def: val2\n" +
|
||||
" '0': zero\n" +
|
||||
" '1': one\n"
|
||||
,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void lineComment() throws Exception {
|
||||
assertConversion(
|
||||
"some.thing=vvvv\n" +
|
||||
"# Line comment\n" +
|
||||
"some.other.thing=blah\n"
|
||||
, // ==>
|
||||
"# Conversion to YAML from Properties formar report\n" +
|
||||
"# Warnings:\n" +
|
||||
"# - The yaml file had comments which are lost in the refactoring!\n" +
|
||||
"some:\n" +
|
||||
" thing: vvvv\n" +
|
||||
" other:\n" +
|
||||
" thing: blah\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2024 Broadcom, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* https://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Broadcom, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.properties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.lsp4j.Command;
|
||||
import org.eclipse.lsp4j.ShowDocumentResult;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
|
||||
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness;
|
||||
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@BootLanguageServerTest
|
||||
@Import({SymbolProviderTestConf.class})
|
||||
public class YamlToPropertiesCommandTest {
|
||||
|
||||
private static final String FILENAME_SUFFIX = "-conversion-test";
|
||||
|
||||
@Autowired private LanguageServerHarness harness;
|
||||
@Autowired private JavaProjectFinder projectFinder;
|
||||
|
||||
private File directory;
|
||||
|
||||
@BeforeEach public void setup() throws Exception {
|
||||
harness.intialize(null);
|
||||
|
||||
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-validations/").toURI());
|
||||
|
||||
Files.walk(directory.toPath(), Integer.MAX_VALUE).filter(Files::isRegularFile).filter(p -> p.getFileName().toString().contains(FILENAME_SUFFIX)).forEach(t -> {
|
||||
try {
|
||||
Files.delete(t);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
String projectDir = directory.toURI().toString();
|
||||
|
||||
// trigger project creation
|
||||
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
|
||||
}
|
||||
|
||||
@AfterEach public void tearDown() throws Exception {
|
||||
if (directory != null && directory.exists()) {
|
||||
Files.walk(directory.toPath(), Integer.MAX_VALUE).filter(Files::isRegularFile).filter(p -> p.getFileName().toString().contains(FILENAME_SUFFIX)).forEach(t -> {
|
||||
try {
|
||||
Files.delete(t);
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test void simple() throws Exception {
|
||||
assertConversion("""
|
||||
some:
|
||||
other:
|
||||
thing: blah
|
||||
thing: vvvv
|
||||
""",
|
||||
"""
|
||||
some.other.thing=blah
|
||||
some.thing=vvvv
|
||||
""",
|
||||
true);
|
||||
}
|
||||
|
||||
@Test void noReplacement() throws Exception {
|
||||
assertConversion("""
|
||||
some:
|
||||
other:
|
||||
thing: blah
|
||||
thing: vvvv
|
||||
""",
|
||||
"""
|
||||
some.other.thing=blah
|
||||
some.thing=vvvv
|
||||
""",
|
||||
false);
|
||||
}
|
||||
|
||||
@Test public void almostHasComments() throws Exception {
|
||||
assertConversion(
|
||||
"my:\n" +
|
||||
" goodbye: 'See ya # later'\n" +
|
||||
" hello: Good morning!\n"
|
||||
, // ==>
|
||||
"my.goodbye=See ya \\# later\n" +
|
||||
"my.hello=Good morning\\!\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void listItems() throws Exception {
|
||||
assertConversion(
|
||||
"some:\n" +
|
||||
" thing:\n" +
|
||||
" - a: first-a\n" +
|
||||
" b: first-b\n" +
|
||||
" - a: second-a\n" +
|
||||
" b: second-b\n"
|
||||
, // ==>
|
||||
"some.thing[0].a=first-a\n" +
|
||||
"some.thing[0].b=first-b\n" +
|
||||
"some.thing[1].a=second-a\n" +
|
||||
"some.thing[1].b=second-b\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void list() throws Exception {
|
||||
assertConversion(
|
||||
"some:\n" +
|
||||
" property:\n" +
|
||||
" - something\n" +
|
||||
" - something-else\n"
|
||||
, // ==>
|
||||
"some.property[0]=something\n" +
|
||||
"some.property[1]=something-else\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void mapAndSequenceConflict() throws Exception {
|
||||
assertConversion(
|
||||
"some:\n" +
|
||||
" property:\n" +
|
||||
" '0': zero\n" +
|
||||
" '1': one\n" +
|
||||
" abc: val1\n" +
|
||||
" def: val2\n"
|
||||
,
|
||||
"some.property.0=zero\n" +
|
||||
"some.property.1=one\n" +
|
||||
"some.property.abc=val1\n" +
|
||||
"some.property.def=val2\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test public void multipleDocsConversion() throws Exception {
|
||||
assertConversion(
|
||||
"some:\n" +
|
||||
" other:\n" +
|
||||
" thing: blah\n" +
|
||||
" thing: vvvv\n" +
|
||||
"\n" +
|
||||
"---\n" +
|
||||
"some:\n" +
|
||||
" other:\n" +
|
||||
" thing: blah\n" +
|
||||
" thing: vvvv\n" +
|
||||
"\n" +
|
||||
"---\n" +
|
||||
"some:\n" +
|
||||
" other:\n" +
|
||||
" thing: blah\n" +
|
||||
" thing: vvvv\n"
|
||||
, // ==>
|
||||
"some.other.thing=blah\n" +
|
||||
"some.thing=vvvv\n" +
|
||||
"#---\n" +
|
||||
"some.other.thing=blah\n" +
|
||||
"some.thing=vvvv\n" +
|
||||
"#---\n" +
|
||||
"some.other.thing=blah\n" +
|
||||
"some.thing=vvvv\n",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@Test void lineComment() throws Exception {
|
||||
assertConversion("""
|
||||
some:
|
||||
# Comment about line
|
||||
other:
|
||||
thing: blah
|
||||
thing: vvvv
|
||||
""",
|
||||
"""
|
||||
# Conversion to YAML from Properties formar report
|
||||
# Warnings:
|
||||
# - The yaml file had comments which are lost in the refactoring!
|
||||
some.other.thing=blah
|
||||
some.thing=vvvv
|
||||
""",
|
||||
true);
|
||||
}
|
||||
|
||||
@Test void inlineComment() throws Exception {
|
||||
assertConversion("""
|
||||
some:
|
||||
other:
|
||||
thing: blah # Inline Comment
|
||||
thing: vvvv
|
||||
""",
|
||||
"""
|
||||
# Conversion to YAML from Properties formar report
|
||||
# Warnings:
|
||||
# - The yaml file had comments which are lost in the refactoring!
|
||||
some.other.thing=blah
|
||||
some.thing=vvvv
|
||||
""",
|
||||
true);
|
||||
}
|
||||
|
||||
void assertConversion(String yamlContent, String propsContent, boolean replace) throws Exception {
|
||||
Path yamlFilePath = directory.toPath().resolve("src/main/resources/application" + FILENAME_SUFFIX + ".yml");
|
||||
Path propsFilePath = directory.toPath().resolve("src/main/resources/application" + FILENAME_SUFFIX + ".properties");
|
||||
|
||||
if (!Files.exists(yamlFilePath)) {
|
||||
Files.createDirectories(yamlFilePath.getParent());
|
||||
Files.createFile(yamlFilePath);
|
||||
Files.write(yamlFilePath, yamlContent.getBytes(), StandardOpenOption.APPEND);
|
||||
}
|
||||
|
||||
Command cmd = new Command();
|
||||
cmd.setCommand(YamlToPropertiesCommand.CMD_YAML_TO_PROPS);
|
||||
cmd.setArguments(List.of(yamlFilePath.toUri().toASCIIString(), propsFilePath.toUri().toASCIIString(), replace));
|
||||
cmd.setTitle("Convert .yaml to .properties");
|
||||
|
||||
ShowDocumentResult res = (ShowDocumentResult) harness.perform(cmd);
|
||||
|
||||
assertThat(res.isSuccess()).isTrue();
|
||||
|
||||
assertThat(Files.exists(propsFilePath)).isTrue();
|
||||
if (replace) {
|
||||
assertThat(Files.exists(yamlFilePath)).isFalse();
|
||||
} else {
|
||||
assertThat(Files.exists(yamlFilePath)).isTrue();
|
||||
}
|
||||
|
||||
assertThat(Files.readString(propsFilePath)).isEqualTo(propsContent);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user