Skip to content

Commit 746fbaa

Browse files
Add IConstraintSetBuilder for programmatic constraint set construction
Introduces a fluent builder API for creating IConstraintSet instances programmatically in tests, eliminating the need for XMLBeans dependency when testing constraint-related functionality. New classes: - IConstraintSetBuilder: Main builder interface for constraint sets - IContextBuilder: Builder for constraint contexts with metapath targeting - ConstraintSetBuilder: Implementation that builds MetaConstraintSet - ContextBuilder: Implementation that builds MetaConstraintSet.Context The builder leverages existing constraint builders (IAllowedValuesConstraint, IMatchesConstraint, etc.) via a generic constraint() method that accepts any AbstractConstraintBuilder. Also migrates ExternalConstraintsModulePostProcessorTest to use the new programmatic builder instead of XmlMetaConstraintLoader.
1 parent eb67ca8 commit 746fbaa

File tree

7 files changed

+551
-15
lines changed

7 files changed

+551
-15
lines changed

core/src/test/java/gov/nist/secauto/metaschema/core/model/constraint/ExternalConstraintsModulePostProcessorTest.java

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,59 @@
77

88
import static org.junit.jupiter.api.Assertions.assertEquals;
99

10+
import gov.nist.secauto.metaschema.core.datatype.markup.MarkupLine;
11+
import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
1012
import gov.nist.secauto.metaschema.core.model.IAssemblyDefinition;
1113
import gov.nist.secauto.metaschema.core.model.IModule;
1214
import gov.nist.secauto.metaschema.core.model.ISource;
13-
import gov.nist.secauto.metaschema.core.model.MetaschemaException;
14-
import gov.nist.secauto.metaschema.core.model.xml.XmlMetaConstraintLoader;
1515
import gov.nist.secauto.metaschema.core.qname.IEnhancedQName;
16-
import gov.nist.secauto.metaschema.core.testsupport.builder.IModuleBuilder;
1716
import gov.nist.secauto.metaschema.core.testsupport.MockedModelTestSupport;
17+
import gov.nist.secauto.metaschema.core.testsupport.builder.IModuleBuilder;
1818
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
1919

2020
import org.junit.jupiter.api.Test;
2121

22-
import java.io.IOException;
2322
import java.net.URI;
24-
import java.nio.file.Paths;
23+
import java.util.Collections;
2524
import java.util.List;
2625

2726
class ExternalConstraintsModulePostProcessorTest {
2827

2928
private static final String TEST_NAMESPACE = "http://csrc.nist.gov/ns/test/metaschema/constraint-targeting-test";
3029

3130
@Test
32-
void test() throws MetaschemaException, IOException {
33-
// Load external constraints from XML
34-
List<IConstraintSet> constraints
35-
= new XmlMetaConstraintLoader().load(ObjectUtils.notNull(
36-
Paths.get("src/test/resources/content/issue184-constraints.xml")));
37-
38-
// Build module programmatically instead of loading from XML
31+
void test() {
32+
// Build constraints programmatically instead of loading from XML
33+
// Original XML:
34+
// <context>
35+
// <metapath target="//*"/>
36+
// <constraints>
37+
// <allowed-values target="@value">
38+
// <enum value="value1">Value #1</enum>
39+
// </allowed-values>
40+
// </constraints>
41+
// </context>
3942
MockedModelTestSupport mocking = new MockedModelTestSupport();
40-
ISource source = ISource.externalSource(URI.create(TEST_NAMESPACE));
43+
ISource constraintSource = ISource.externalSource(URI.create(TEST_NAMESPACE + "/constraints"));
44+
45+
IConstraintSet constraintSet = mocking.constraintSet()
46+
.source(constraintSource)
47+
.context(ctx -> ctx
48+
.metapath("//*")
49+
.constraint(IAllowedValuesConstraint.builder()
50+
.source(constraintSource)
51+
.target(IMetapathExpression.compile("@value"))
52+
.allowedValue(IAllowedValue.of("value1", MarkupLine.fromMarkdown("Value #1"), null))))
53+
.build();
54+
55+
// Build module programmatically
56+
ISource moduleSource = ISource.externalSource(URI.create(TEST_NAMESPACE));
4157

4258
IModule module = IModuleBuilder.builder()
4359
.namespace(TEST_NAMESPACE)
4460
.shortName("constraint-targeting-test")
4561
.version("1.0.0")
46-
.source(source)
62+
.source(moduleSource)
4763
.assembly(mocking.assembly()
4864
.name("a")
4965
.rootName("a")
@@ -54,7 +70,7 @@ void test() throws MetaschemaException, IOException {
5470

5571
// Apply external constraints to the module
5672
ExternalConstraintsModulePostProcessor postProcessor
57-
= new ExternalConstraintsModulePostProcessor(constraints);
73+
= new ExternalConstraintsModulePostProcessor(Collections.singletonList(constraintSet));
5874
postProcessor.processModule(module);
5975

6076
// Verify constraint was applied
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* SPDX-FileCopyrightText: none
3+
* SPDX-License-Identifier: CC0-1.0
4+
*/
5+
6+
package gov.nist.secauto.metaschema.core.testsupport.builder;
7+
8+
import gov.nist.secauto.metaschema.core.model.ISource;
9+
import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
10+
import gov.nist.secauto.metaschema.core.model.constraint.MetaConstraintSet;
11+
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
12+
13+
import java.util.ArrayList;
14+
import java.util.Arrays;
15+
import java.util.List;
16+
import java.util.function.Consumer;
17+
18+
import edu.umd.cs.findbugs.annotations.NonNull;
19+
20+
/**
21+
* Implementation of {@link IConstraintSetBuilder} for creating constraint sets
22+
* programmatically.
23+
*/
24+
public class ConstraintSetBuilder implements IConstraintSetBuilder {
25+
private ISource source;
26+
@NonNull
27+
private final List<IConstraintSet> imports = new ArrayList<>();
28+
@NonNull
29+
private final List<ContextBuilder> contexts = new ArrayList<>();
30+
31+
/**
32+
* Construct a new builder.
33+
*/
34+
public ConstraintSetBuilder() {
35+
// default constructor
36+
}
37+
38+
@Override
39+
@NonNull
40+
public IConstraintSetBuilder source(@NonNull ISource source) {
41+
this.source = source;
42+
return this;
43+
}
44+
45+
@Override
46+
@NonNull
47+
public IConstraintSetBuilder imports(@NonNull IConstraintSet... imports) {
48+
this.imports.addAll(Arrays.asList(imports));
49+
return this;
50+
}
51+
52+
@Override
53+
@NonNull
54+
public IConstraintSetBuilder context(@NonNull Consumer<IContextBuilder> contextConfigurer) {
55+
ContextBuilder contextBuilder = new ContextBuilder(ObjectUtils.requireNonNull(source));
56+
contextConfigurer.accept(contextBuilder);
57+
this.contexts.add(contextBuilder);
58+
return this;
59+
}
60+
61+
@Override
62+
@NonNull
63+
public IConstraintSet build() {
64+
ISource constraintSource = ObjectUtils.requireNonNull(source, "source must be set");
65+
66+
// Build contexts
67+
List<MetaConstraintSet.Context> builtContexts = new ArrayList<>();
68+
for (ContextBuilder contextBuilder : contexts) {
69+
builtContexts.add(contextBuilder.build(null));
70+
}
71+
72+
return new MetaConstraintSet(constraintSource, imports, builtContexts);
73+
}
74+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* SPDX-FileCopyrightText: none
3+
* SPDX-License-Identifier: CC0-1.0
4+
*/
5+
6+
package gov.nist.secauto.metaschema.core.testsupport.builder;
7+
8+
import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
9+
import gov.nist.secauto.metaschema.core.model.ISource;
10+
import gov.nist.secauto.metaschema.core.model.constraint.AbstractConstraintBuilder;
11+
import gov.nist.secauto.metaschema.core.model.constraint.AssemblyConstraintSet;
12+
import gov.nist.secauto.metaschema.core.model.constraint.IAllowedValuesConstraint;
13+
import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
14+
import gov.nist.secauto.metaschema.core.model.constraint.IExpectConstraint;
15+
import gov.nist.secauto.metaschema.core.model.constraint.IIndexHasKeyConstraint;
16+
import gov.nist.secauto.metaschema.core.model.constraint.IMatchesConstraint;
17+
import gov.nist.secauto.metaschema.core.model.constraint.IModelConstrained;
18+
import gov.nist.secauto.metaschema.core.model.constraint.MetaConstraintSet;
19+
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.function.Consumer;
24+
25+
import edu.umd.cs.findbugs.annotations.NonNull;
26+
import edu.umd.cs.findbugs.annotations.Nullable;
27+
28+
/**
29+
* Implementation of {@link IContextBuilder} for creating constraint contexts.
30+
*/
31+
public class ContextBuilder implements IContextBuilder {
32+
@NonNull
33+
private final ISource source;
34+
@NonNull
35+
private final List<IMetapathExpression> metapaths = new ArrayList<>();
36+
@NonNull
37+
private final List<IConstraint> constraints = new ArrayList<>();
38+
@NonNull
39+
private final List<ContextBuilder> childContexts = new ArrayList<>();
40+
41+
/**
42+
* Construct a new context builder.
43+
*
44+
* @param source
45+
* the source for constraints in this context
46+
*/
47+
public ContextBuilder(@NonNull ISource source) {
48+
this.source = source;
49+
}
50+
51+
@Override
52+
@NonNull
53+
public IContextBuilder metapath(@NonNull String target) {
54+
this.metapaths.add(IMetapathExpression.lazyCompile(target, source.getStaticContext()));
55+
return this;
56+
}
57+
58+
@Override
59+
@NonNull
60+
public <B extends AbstractConstraintBuilder<B, C>, C extends IConstraint> IContextBuilder constraint(
61+
@NonNull AbstractConstraintBuilder<B, C> constraintBuilder) {
62+
this.constraints.add(constraintBuilder.build());
63+
return this;
64+
}
65+
66+
@Override
67+
@NonNull
68+
public IContextBuilder childContext(@NonNull Consumer<IContextBuilder> childConfigurer) {
69+
ContextBuilder childBuilder = new ContextBuilder(source);
70+
childConfigurer.accept(childBuilder);
71+
this.childContexts.add(childBuilder);
72+
return this;
73+
}
74+
75+
/**
76+
* Build the context.
77+
*
78+
* @param parent
79+
* the parent context, or null if this is a top-level context
80+
* @return the built context
81+
*/
82+
@NonNull
83+
MetaConstraintSet.Context build(@Nullable MetaConstraintSet.Context parent) {
84+
// Create the constraint set for this context
85+
IModelConstrained modelConstrained = new AssemblyConstraintSet(source);
86+
87+
// Add constraints to the model
88+
for (IConstraint constraint : constraints) {
89+
addConstraint(modelConstrained, constraint);
90+
}
91+
92+
// Create the context
93+
MetaConstraintSet.Context context = new MetaConstraintSet.Context(
94+
parent,
95+
source,
96+
ObjectUtils.notNull(metapaths),
97+
modelConstrained);
98+
99+
// Build and add child contexts
100+
List<MetaConstraintSet.Context> builtChildren = new ArrayList<>();
101+
for (ContextBuilder childBuilder : childContexts) {
102+
builtChildren.add(childBuilder.build(context));
103+
}
104+
context.addAll(builtChildren);
105+
106+
return context;
107+
}
108+
109+
private static void addConstraint(@NonNull IModelConstrained modelConstrained, @NonNull IConstraint constraint) {
110+
if (constraint instanceof IAllowedValuesConstraint) {
111+
modelConstrained.addConstraint((IAllowedValuesConstraint) constraint);
112+
} else if (constraint instanceof IMatchesConstraint) {
113+
modelConstrained.addConstraint((IMatchesConstraint) constraint);
114+
} else if (constraint instanceof IExpectConstraint) {
115+
modelConstrained.addConstraint((IExpectConstraint) constraint);
116+
} else if (constraint instanceof IIndexHasKeyConstraint) {
117+
modelConstrained.addConstraint((IIndexHasKeyConstraint) constraint);
118+
} else {
119+
throw new UnsupportedOperationException(
120+
"Unsupported constraint type: " + constraint.getClass().getName());
121+
}
122+
}
123+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* SPDX-FileCopyrightText: none
3+
* SPDX-License-Identifier: CC0-1.0
4+
*/
5+
6+
package gov.nist.secauto.metaschema.core.testsupport.builder;
7+
8+
import gov.nist.secauto.metaschema.core.model.ISource;
9+
import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
10+
11+
import java.util.function.Consumer;
12+
13+
import edu.umd.cs.findbugs.annotations.NonNull;
14+
15+
/**
16+
* A builder for creating {@link IConstraintSet} instances programmatically.
17+
* <p>
18+
* This builder is intended for test support, allowing constraint sets to be
19+
* constructed without loading XML files.
20+
*/
21+
public interface IConstraintSetBuilder {
22+
23+
/**
24+
* Set the source for the constraint set.
25+
*
26+
* @param source
27+
* the source information
28+
* @return this builder
29+
*/
30+
@NonNull
31+
IConstraintSetBuilder source(@NonNull ISource source);
32+
33+
/**
34+
* Add an imported constraint set.
35+
*
36+
* @param imports
37+
* the constraint sets to import
38+
* @return this builder
39+
*/
40+
@NonNull
41+
IConstraintSetBuilder imports(@NonNull IConstraintSet... imports);
42+
43+
/**
44+
* Add a context to the constraint set.
45+
*
46+
* @param contextConfigurer
47+
* a consumer that configures the context
48+
* @return this builder
49+
*/
50+
@NonNull
51+
IConstraintSetBuilder context(@NonNull Consumer<IContextBuilder> contextConfigurer);
52+
53+
/**
54+
* Build the constraint set.
55+
*
56+
* @return the constructed constraint set
57+
*/
58+
@NonNull
59+
IConstraintSet build();
60+
61+
/**
62+
* Create a new constraint set builder.
63+
*
64+
* @return the builder
65+
*/
66+
@NonNull
67+
static IConstraintSetBuilder builder() {
68+
return new ConstraintSetBuilder();
69+
}
70+
}

0 commit comments

Comments
 (0)