Refactor / simplify how Yaml schema Constraints work:
- simplify access to DynamicSchemaContext. - allow constraints on non-map type nodes. These changes pave the way to checking inertaction constraint on job-name used in 'passed' attribute.
This commit is contained in:
@@ -252,10 +252,9 @@ public class SchemaBasedYamlASTReconciler implements YamlASTReconciler {
|
||||
}
|
||||
|
||||
//Check for other constraints attached to the type
|
||||
for (SchemaContextAware<Constraint> _constraint : typeUtil.getConstraints(type)) {
|
||||
Constraint constraint = _constraint.withContext(dc);
|
||||
for (Constraint constraint : typeUtil.getConstraints(type)) {
|
||||
if (constraint!=null) {
|
||||
constraint.verify(dc.getDocument(), parent, map, type, foundProps, problems);
|
||||
constraint.verify(dc, parent, map, type, problems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ public class YTypeFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SchemaContextAware<Constraint>> getConstraints(YType type) {
|
||||
public List<Constraint> getConstraints(YType type) {
|
||||
return ((AbstractType)type).getConstraints();
|
||||
}
|
||||
};
|
||||
@@ -218,7 +218,7 @@ public class YTypeFactory {
|
||||
private Map<String, YTypedProperty> cachedPropertyMap;
|
||||
private SchemaContextAware<Callable<Collection<YValueHint>>> hintProvider;
|
||||
|
||||
private List<SchemaContextAware<Constraint>> constraints = new ArrayList<>(2);
|
||||
private List<Constraint> constraints = new ArrayList<>(2);
|
||||
|
||||
public boolean isSequenceable() {
|
||||
return false;
|
||||
@@ -282,7 +282,7 @@ public class YTypeFactory {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
public List<SchemaContextAware<Constraint>> getConstraints() {
|
||||
public List<Constraint> getConstraints() {
|
||||
return ImmutableList.copyOf(constraints);
|
||||
}
|
||||
|
||||
@@ -357,12 +357,12 @@ public class YTypeFactory {
|
||||
return parser == null ? null : parser.withContext(dc);
|
||||
}
|
||||
|
||||
public void require(SchemaContextAware<Constraint> dynamicConstraint) {
|
||||
public void require(Constraint dynamicConstraint) {
|
||||
this.constraints.add(dynamicConstraint);
|
||||
}
|
||||
|
||||
public void requireOneOf(String... properties) {
|
||||
this.constraints.add(SchemaContextAware.just(Constraints.requireOneOf(properties)));
|
||||
this.constraints.add(Constraints.requireOneOf(properties));
|
||||
}
|
||||
|
||||
public String[] getPropertyNames() {
|
||||
|
||||
@@ -46,5 +46,5 @@ public interface YTypeUtil {
|
||||
* should be returned.
|
||||
*/
|
||||
YType inferMoreSpecificType(YType type, DynamicSchemaContext dc);
|
||||
List<SchemaContextAware<Constraint>> getConstraints(YType type);
|
||||
List<Constraint> getConstraints(YType type);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.util.Set;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
@@ -32,12 +33,11 @@ public interface Constraint {
|
||||
* constraint is satisfied. When the constrain is not satisfied they should report any
|
||||
* violations by adding problems to the provide {@link IProblemCollector}.
|
||||
*
|
||||
* @param map The node being validated
|
||||
* @param node The node being validated
|
||||
* @param type The inferred type of the node.
|
||||
* @param foundProps The properties this node defines.
|
||||
* @param problems Problem collector where to which the constraint should add the validation problems it finds.
|
||||
*/
|
||||
void verify(IDocument doc, Node parent, MappingNode map, YType type, Set<String> foundProps,
|
||||
IProblemCollector problems);
|
||||
void verify(DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems);
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.yaml.ast.NodeUtil;
|
||||
import org.springframework.ide.vscode.commons.yaml.reconcile.YamlSchemaProblems;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.DynamicSchemaContext;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.SchemaContextAware;
|
||||
import org.springframework.ide.vscode.commons.yaml.schema.YType;
|
||||
import org.yaml.snakeyaml.nodes.MappingNode;
|
||||
import org.yaml.snakeyaml.nodes.Node;
|
||||
@@ -62,23 +64,28 @@ public class Constraints {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(IDocument doc, Node parent, MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) {
|
||||
List<String> requiredProps = Arrays.asList(_requiredProps);
|
||||
long foundPropsCount = requiredProps.stream()
|
||||
.filter(foundProps::contains)
|
||||
.count();
|
||||
if (foundPropsCount==0) {
|
||||
if (!allowFewer) {
|
||||
problems.accept(missingProperty(
|
||||
"One of "+requiredProps+" is required for '"+type+"'", doc, parent, map));
|
||||
}
|
||||
} else if (foundPropsCount>1) {
|
||||
//Mark each of the found keys as a violation:
|
||||
for (NodeTuple entry : map.getValue()) {
|
||||
String key = NodeUtil.asScalar(entry.getKeyNode());
|
||||
if (key!=null && requiredProps.contains(key)) {
|
||||
problems.accept(problem(EXTRA_PROPERTY,
|
||||
"Only one of "+requiredProps+" should be defined for '"+type+"'", entry.getKeyNode()));
|
||||
public void verify(DynamicSchemaContext dc, Node parent, Node _map, YType type, IProblemCollector problems) {
|
||||
IDocument doc = dc.getDocument();
|
||||
Set<String> foundProps = dc.getDefinedProperties();
|
||||
if (_map instanceof MappingNode) {
|
||||
MappingNode map = (MappingNode) _map;
|
||||
List<String> requiredProps = Arrays.asList(_requiredProps);
|
||||
long foundPropsCount = requiredProps.stream()
|
||||
.filter(foundProps::contains)
|
||||
.count();
|
||||
if (foundPropsCount==0) {
|
||||
if (!allowFewer) {
|
||||
problems.accept(missingProperty(
|
||||
"One of "+requiredProps+" is required for '"+type+"'", doc, parent, map));
|
||||
}
|
||||
} else if (foundPropsCount>1) {
|
||||
//Mark each of the found keys as a violation:
|
||||
for (NodeTuple entry : map.getValue()) {
|
||||
String key = NodeUtil.asScalar(entry.getKeyNode());
|
||||
if (key!=null && requiredProps.contains(key)) {
|
||||
problems.accept(problem(EXTRA_PROPERTY,
|
||||
"Only one of "+requiredProps+" should be defined for '"+type+"'", entry.getKeyNode()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,14 +94,32 @@ public class Constraints {
|
||||
|
||||
public static Constraint deprecated(Function<String, String> messageFormatter, String... _deprecatedNames) {
|
||||
Set<String> deprecatedNames = ImmutableSet.copyOf(_deprecatedNames);
|
||||
return (IDocument doc, Node parent, MappingNode map, YType type, Set<String> foundProps, IProblemCollector problems) -> {
|
||||
for (NodeTuple prop : map.getValue()) {
|
||||
Node keyNode = prop.getKeyNode();
|
||||
String name = NodeUtil.asScalar(keyNode);
|
||||
if (deprecatedNames.contains(name)) {
|
||||
problems.accept(YamlSchemaProblems.deprecatedProperty(messageFormatter.apply(name), keyNode));
|
||||
return (DynamicSchemaContext dc, Node parent, Node _map, YType type, IProblemCollector problems) -> {
|
||||
if (_map instanceof MappingNode) {
|
||||
MappingNode map = (MappingNode) _map;
|
||||
for (NodeTuple prop : map.getValue()) {
|
||||
Node keyNode = prop.getKeyNode();
|
||||
String name = NodeUtil.asScalar(keyNode);
|
||||
if (deprecatedNames.contains(name)) {
|
||||
problems.accept(YamlSchemaProblems.deprecatedProperty(messageFormatter.apply(name), keyNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated because you shouldn't need to use this method to create a {@link SchemaContextAware} Constraint.
|
||||
* A Constraint itself is already implicitly aware of the {@link DynamicSchemaContext} (i.e. it already receives
|
||||
* the {@link DynamicSchemaContext} as a parameter to its verify method.
|
||||
* <p>
|
||||
* So instead of using this method to get a hold of the {@link DynamicSchemaContext} simply use the context
|
||||
* passed to your constraint instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static Constraint schemaContextAware(SchemaContextAware<Constraint> dispatcher) {
|
||||
return (DynamicSchemaContext dc, Node parent, Node node, YType type, IProblemCollector problems) -> {
|
||||
dispatcher.withContext(dc).verify(dc, parent, node, type, problems);
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user