Proper support for Set<?> types in application.yml editor
This commit is contained in:
@@ -31,8 +31,12 @@ import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.BeanPropertyN
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil.EnumCaseMode;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypedProperty;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParseException;
|
||||
import org.springframework.ide.vscode.commons.util.ValueParser;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.Kind;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeRef.TupleValueRef;
|
||||
@@ -66,21 +70,21 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
reconcile(ast, nav);
|
||||
}
|
||||
|
||||
protected void reconcile(YamlFileAST ast, IndexNavigator nav) {
|
||||
List<Node> nodes = ast.getNodes();
|
||||
protected void reconcile(YamlFileAST root, IndexNavigator nav) {
|
||||
List<Node> nodes = root.getNodes();
|
||||
if (nodes!=null && !nodes.isEmpty()) {
|
||||
for (Node node : nodes) {
|
||||
reconcile(node, nav);
|
||||
reconcile(root, node, nav);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void reconcile(Node node, IndexNavigator nav) {
|
||||
protected void reconcile(YamlFileAST root, Node node, IndexNavigator nav) {
|
||||
switch (node.getNodeId()) {
|
||||
case mapping:
|
||||
checkForDuplicateKeys((MappingNode)node);
|
||||
for (NodeTuple entry : ((MappingNode)node).getValue()) {
|
||||
reconcile(entry, nav);
|
||||
reconcile(root, entry, nav);
|
||||
}
|
||||
break;
|
||||
case scalar:
|
||||
@@ -121,7 +125,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
return propName!=null && propName.equals("spring.profiles");
|
||||
}
|
||||
|
||||
private void reconcile(NodeTuple entry, IndexNavigator nav) {
|
||||
private void reconcile(YamlFileAST root, NodeTuple entry, IndexNavigator nav) {
|
||||
Node keyNode = entry.getKeyNode();
|
||||
String key = asScalar(keyNode);
|
||||
if (key==null) {
|
||||
@@ -153,12 +157,12 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
if (match.isDeprecated()) {
|
||||
deprecatedProperty(match, keyNode);
|
||||
}
|
||||
reconcile(entry.getValueNode(), type);
|
||||
reconcile(root, entry.getValueNode(), type);
|
||||
} else if (extension!=null) {
|
||||
//We don't really care about the extension only about the fact that it
|
||||
// exists and so it is meaningful to continue checking...
|
||||
Node valueNode = entry.getValueNode();
|
||||
reconcile(valueNode, subNav);
|
||||
reconcile(root, valueNode, subNav);
|
||||
} else {
|
||||
//both are null, this means there's no valid property with the current prefix
|
||||
//whether exact or extending it with further navigation
|
||||
@@ -170,17 +174,17 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
/**
|
||||
* Reconcile a node given the type that we expect the node to be.
|
||||
*/
|
||||
private void reconcile(Node node, Type type) {
|
||||
private void reconcile(YamlFileAST root, Node node, Type type) {
|
||||
if (type!=null) {
|
||||
switch (node.getNodeId()) {
|
||||
case scalar:
|
||||
reconcile((ScalarNode)node, type);
|
||||
reconcile(root, (ScalarNode)node, type);
|
||||
break;
|
||||
case sequence:
|
||||
reconcile((SequenceNode)node, type);
|
||||
reconcile(root, (SequenceNode)node, type);
|
||||
break;
|
||||
case mapping:
|
||||
reconcile((MappingNode)node, type);
|
||||
reconcile(root, (MappingNode)node, type);
|
||||
break;
|
||||
case anchor:
|
||||
//TODO: what should we do with anchor nodes
|
||||
@@ -191,7 +195,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
}
|
||||
|
||||
private void reconcile(MappingNode mapping, Type type) {
|
||||
private void reconcile(YamlFileAST root, MappingNode mapping, Type type) {
|
||||
checkForDuplicateKeys(mapping);
|
||||
if (typeUtil.isAtomic(type)) {
|
||||
expectTypeFoundMapping(type, mapping);
|
||||
@@ -200,7 +204,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
Type valueType = TypeUtil.getDomainType(type);
|
||||
if (keyType!=null) {
|
||||
for (NodeTuple entry : mapping.getValue()) {
|
||||
reconcile(entry.getKeyNode(), keyType);
|
||||
reconcile(root, entry.getKeyNode(), keyType);
|
||||
}
|
||||
}
|
||||
if (valueType!=null) {
|
||||
@@ -217,7 +221,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
nestedValueType = type;
|
||||
}
|
||||
}
|
||||
reconcile(entry.getValueNode(), nestedValueType);
|
||||
reconcile(root, entry.getValueNode(), nestedValueType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -239,7 +243,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
if (typedProperty.isDeprecated()) {
|
||||
deprecatedProperty(type, typedProperty, keyNode);
|
||||
}
|
||||
reconcile(valNode, typedProperty.getType());
|
||||
reconcile(root, valNode, typedProperty.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -248,14 +252,14 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
}
|
||||
|
||||
private void reconcile(SequenceNode seq, Type type) {
|
||||
private void reconcile(YamlFileAST root, SequenceNode seq, Type type) {
|
||||
if (typeUtil.isAtomic(type)) {
|
||||
expectTypeFoundSequence(type, seq);
|
||||
} else if (TypeUtil.isSequencable(type)) {
|
||||
Type domainType = TypeUtil.getDomainType(type);
|
||||
if (domainType!=null) {
|
||||
for (Node element : seq.getValue()) {
|
||||
reconcile(element, domainType);
|
||||
reconcile(root, element, domainType);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -264,7 +268,7 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
|
||||
|
||||
private void reconcile(ScalarNode scalar, Type type) {
|
||||
private void reconcile(YamlFileAST root, ScalarNode scalar, Type type) {
|
||||
String stringValue = scalar.getValue();
|
||||
if (!hasPlaceHolder(stringValue)) { //don't check anything with placeholder expressions in it
|
||||
ValueParser valueParser = typeUtil.getValueParser(type);
|
||||
@@ -274,6 +278,8 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
// expected type
|
||||
try {
|
||||
valueParser.parse(stringValue);
|
||||
} catch (ValueParseException e) {
|
||||
valueParseError(root, scalar, e);
|
||||
} catch (Exception e) {
|
||||
//Couldn't parse
|
||||
valueTypeMismatch(type, scalar);
|
||||
@@ -300,6 +306,12 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
expectType(ApplicationYamlProblemType.YAML_VALUE_TYPE_MISMATCH, type, scalar);
|
||||
}
|
||||
|
||||
private void valueParseError(YamlFileAST root, ScalarNode scalar, ValueParseException e) {
|
||||
IDocument doc = root.getDocument();
|
||||
DocumentRegion containingRegion = new DocumentRegion(doc, scalar.getStartMark().getIndex(), scalar.getEndMark().getIndex());
|
||||
problems.accept(problem(ApplicationYamlProblemType.YAML_VALUE_TYPE_MISMATCH, e.getHighlightRegion(containingRegion), ExceptionUtil.getMessage(e)));
|
||||
}
|
||||
|
||||
private void unkownProperty(Node node, String name, NodeTuple entry) {
|
||||
SpringPropertyProblem p = problem(ApplicationYamlProblemType.YAML_UNKNOWN_PROPERTY, node, "Unknown property '"+name+"'");
|
||||
p.setPropertyName(extendForQuickfix(StringUtil.camelCaseToHyphens(name), entry.getValueNode()));
|
||||
@@ -378,6 +390,10 @@ public class ApplicationYamlASTReconciler implements YamlASTReconciler {
|
||||
return SpringPropertyProblem.problem(type, msg, start, end-start);
|
||||
}
|
||||
|
||||
private SpringPropertyProblem problem(ApplicationYamlProblemType type, DocumentRegion region, String msg) {
|
||||
return SpringPropertyProblem.problem(type, msg, region.getStart(), region.getLength());
|
||||
}
|
||||
|
||||
private String describe(Node node) {
|
||||
switch (node.getNodeId()) {
|
||||
case scalar:
|
||||
|
||||
@@ -17,6 +17,7 @@ import static org.springframework.ide.vscode.languageserver.testharness.Editor.I
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.core.resources.IProject;
|
||||
import org.eclipse.lsp4j.CompletionItem;
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.junit.Ignore;
|
||||
@@ -29,11 +30,11 @@ import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
|
||||
import org.springframework.ide.vscode.boot.metadata.CachingValueProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
import org.springframework.ide.vscode.boot.properties.BootPropertiesLanguageServerComponents;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.composable.ComposableLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.maven.java.MavenJavaProject;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.languageserver.testharness.Editor;
|
||||
@@ -715,19 +716,9 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
);
|
||||
|
||||
editor.assertProblems(
|
||||
"bad: BLUE|Expecting a 'demo.Color",
|
||||
"Bogus|Expecting a 'demo.Color"
|
||||
"bad: BLUE|demo.Color",
|
||||
"Bogus|demo.Color"
|
||||
);
|
||||
|
||||
/*
|
||||
* TODO: if enums are not sorted by the 3rd party java indexing lib then
|
||||
* perform the commented out test rather than the above
|
||||
*/
|
||||
// editor.assertProblems(
|
||||
// "bad: BLUE|Expecting a 'demo.Color[RED, GREEN, BLUE]' but got a 'Mapping' node",
|
||||
// "Bogus|Expecting a 'demo.Color[RED, GREEN, BLUE]' but got 'Bogus'"
|
||||
// );
|
||||
|
||||
}
|
||||
|
||||
@Test public void testReconcileSkipIfNoMetadata() throws Exception {
|
||||
@@ -3459,7 +3450,7 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
" - GREEN\n"
|
||||
);
|
||||
editor.assertProblems(
|
||||
"not-a-color|Expecting a 'com.wellsfargo.lendingplatform.web.config.Color"
|
||||
"not-a-color|com.wellsfargo.lendingplatform.web.config.Color"
|
||||
);
|
||||
|
||||
editor = newEditor(
|
||||
@@ -3690,6 +3681,69 @@ public class ApplicationYamlEditorTest extends AbstractPropsEditorTest {
|
||||
editor.assertProblems("bogus|Unknown property");
|
||||
}
|
||||
|
||||
@Test public void testSetOfEnumReconcile() throws Exception {
|
||||
String collectionType = "java.util.Set";
|
||||
doCollectionOfEnumReconcileTest(collectionType);
|
||||
}
|
||||
|
||||
@Test public void testListOfEnumReconcile() throws Exception {
|
||||
String collectionType = "java.util.List";
|
||||
doCollectionOfEnumReconcileTest(collectionType);
|
||||
}
|
||||
|
||||
private void doCollectionOfEnumReconcileTest(String collectionType) throws Exception {
|
||||
useProject(createPredefinedMavenProject("enums-boot-1.3.2-app"));
|
||||
data("my.colors", collectionType + "<demo.Color>", null, "Ooh! nice colors!");
|
||||
Editor editor;
|
||||
|
||||
//comma-separated string
|
||||
editor = newEditor(
|
||||
"my:\n" +
|
||||
" colors: red,green,BLUE,not-a-color\n"
|
||||
);
|
||||
editor.assertProblems("not-a-color|demo.Color");
|
||||
|
||||
//comma-separated string
|
||||
editor = newEditor(
|
||||
"my:\n" +
|
||||
" colors: red, green, not-a-color, BLUE\n"
|
||||
);
|
||||
editor.assertProblems("not-a-color|demo.Color");
|
||||
|
||||
//flow list
|
||||
editor = newEditor(
|
||||
"my:\n" +
|
||||
" colors: [red, green, BLUE, not-a-color]"
|
||||
);
|
||||
editor.assertProblems("not-a-color|demo.Color");
|
||||
|
||||
//block list
|
||||
editor = newEditor(
|
||||
"my:\n" +
|
||||
" colors:\n" +
|
||||
" - red\n" +
|
||||
" - green\n" +
|
||||
" - BLUE\n" +
|
||||
" - not-a-color\n"
|
||||
);
|
||||
editor.assertProblems("not-a-color|demo.Color");
|
||||
}
|
||||
|
||||
@Test public void testSetOfEnumCompletions() throws Exception {
|
||||
useProject(createPredefinedMavenProject("enums-boot-1.3.2-app"));
|
||||
data("my.colors", "java.util.Set<demo.Color>", null, "Ooh! nice colors!");
|
||||
|
||||
Editor editor = newEditor(
|
||||
"my:\n" +
|
||||
" colors:\n" +
|
||||
" - <*>"
|
||||
);
|
||||
editor.assertContextualCompletions("<*>", "blue<*>", "green<*>", "red<*>");
|
||||
editor.assertContextualCompletions("b<*>", "blue<*>");
|
||||
editor.assertContextualCompletions("B<*>", "BLUE<*>");
|
||||
}
|
||||
|
||||
|
||||
///////////////// cruft ////////////////////////////////////////////////////////
|
||||
|
||||
private void generateNestedProperties(int levels, String[] names, String prefix) {
|
||||
|
||||
Reference in New Issue
Block a user