diff --git a/oscal b/oscal index d3a2b990..4faaba2c 160000 --- a/oscal +++ b/oscal @@ -1 +1 @@ -Subproject commit d3a2b990e24210c253642451e30ea6db99bd045b +Subproject commit 4faaba2c6e8c866a20e37cc3b9ac28e0f9d4f57e diff --git a/pom.xml b/pom.xml index d07b69b2..a25afd8b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 @@ -13,7 +14,9 @@ jar OSCAL Java Library - A Java library that parses XML, JSON, and YAML data formatted acording to a Metaschema-based model. + A Java library that parses XML, JSON, and YAML data + formatted acording to a Metaschema-based model. + https://github.com/usnistgov/liboscal-java @@ -23,10 +26,12 @@ https://github.com/usnistgov/liboscal-java - scm:git:git@github.com:usnistgov/liboscal-java.git - scm:git:git@github.com:usnistgov/liboscal-java.git - v1.0.4 - + scm:git:git@github.com:usnistgov/liboscal-java.git + + scm:git:git@github.com:usnistgov/liboscal-java.git + + v1.0.4 + @@ -35,7 +40,8 @@ ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + nist-pages @@ -62,7 +68,8 @@ david.waltermire@nist.gov David Waltermire david.waltermire@nist.gov - National Institute of Standards and Technology + National Institute of Standards and Technology + architect developer @@ -115,16 +122,23 @@ UTF-8 0.9.0-SNAPSHOT + + 1.0.1 + 3.12.0 + 13.0.10.Final 23.0.0 2.12.0 5.8.2 1.8.2 + 2.18.0 11.3 + 4.2.0 4.4.0 4.0.rc1 1.3 2.7.0 + 4.7.1.1 master @@ -160,6 +174,32 @@ + + gov.nist.secauto.metaschema + metaschema-java-binding + ${dependency.metaschema-framework.version} + + + com.google.auto.service + auto-service + ${dependency.auto-service.version} + + + org.apache.commons + commons-lang3 + ${dependency.commons-lang3.version} + + + + org.apache.logging.log4j + log4j-api + ${dependency.log4j2.version} + + + org.apache.logging.log4j + log4j-core + ${dependency.log4j2.version} + org.xmlresolver xmlresolver @@ -185,6 +225,13 @@ + + com.github.spotbugs + spotbugs-annotations + ${dependency.spotbugs-annotations.version} + provided + true + @@ -192,24 +239,37 @@ gov.nist.secauto.metaschema metaschema-java-binding - ${dependency.metaschema-framework.version} - org.jetbrains - annotations - ${dependency.jetbrains-annotation.version} - provided + com.google.auto.service + auto-service + true - org.junit.jupiter - junit-jupiter-api + org.apache.commons + commons-lang3 + + + + com.github.spotbugs + spotbugs-annotations + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core test + org.junit.jupiter - junit-jupiter-engine + junit-jupiter-api test @@ -260,7 +320,8 @@ src/main/resources - ${project.build.directory}/generated-resources/oscal + ${project.build.directory}/generated-resources/oscal + **/*.xsd **/*.json @@ -289,8 +350,10 @@ maven-checkstyle-plugin - ${project.build.sourceDirectory} - ${project.build.testSourceDirectory} + ${project.build.sourceDirectory} + + ${project.build.testSourceDirectory} + @@ -325,7 +388,8 @@ false false all - ${project.artifactId}-${project.version}-cyclonedx + ${project.artifactId}-${project.version}-cyclonedx + @@ -336,6 +400,13 @@ + + com.github.spotbugs + spotbugs-maven-plugin + + spotbugs-exclude.xml + + @@ -346,7 +417,8 @@ ${project.basedir}/oscal/.git true - ${project.build.directory}/oscal-git.properties + ${project.build.directory}/oscal-git.properties + true false oscal-git @@ -372,7 +444,8 @@ copy-resources - ${project.build.directory}/generated-resources/oscal/schema/xml + ${project.build.directory}/generated-resources/oscal/schema/xml + ${project.basedir}/oscal/xml/schema @@ -387,7 +460,8 @@ copy-resources - ${project.build.directory}/generated-resources/oscal/schema/json + ${project.build.directory}/generated-resources/oscal/schema/json + ${project.basedir}/oscal/json/schema @@ -426,8 +500,10 @@ wget - https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/xml/NIST_SP-800-53_rev5_catalog.xml - ${project.build.directory}/download/content + https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/xml/NIST_SP-800-53_rev5_catalog.xml + + ${project.build.directory}/download/content + @@ -437,8 +513,10 @@ wget - https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_catalog.json - ${project.build.directory}/download/content + https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_catalog.json + + ${project.build.directory}/download/content + @@ -448,8 +526,10 @@ wget - https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/yaml/NIST_SP-800-53_rev5_catalog.yaml - ${project.build.directory}/download/content + https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/yaml/NIST_SP-800-53_rev5_catalog.yaml + + ${project.build.directory}/download/content + @@ -459,8 +539,10 @@ wget - https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/xml/NIST_SP-800-53_rev5_LOW-baseline_profile.xml - ${project.build.directory}/download/content + https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/xml/NIST_SP-800-53_rev5_LOW-baseline_profile.xml + + ${project.build.directory}/download/content + @@ -470,8 +552,10 @@ wget - https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json - ${project.build.directory}/download/content + https://raw.githubusercontent.com/usnistgov/oscal-content/${oscal-content.commit}/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_MODERATE-baseline_profile.json + + ${project.build.directory}/download/content + @@ -487,9 +571,11 @@ generate-sources - ${project.basedir}/oscal/src/metaschema + ${project.basedir}/oscal/src/metaschema + - ${project.basedir}/src/main/metaschema-bindings/oscal-metaschema-bindings.xml + ${project.basedir}/src/main/metaschema-bindings/oscal-metaschema-bindings.xml + oscal_complete_metaschema.xml @@ -510,7 +596,8 @@ - ${project.build.directory}/generated-sources/metaschema + ${project.build.directory}/generated-sources/metaschema + @@ -523,7 +610,8 @@ - ${project.build.directory}/generated-resources/oscal + ${project.build.directory}/generated-resources/oscal + **/*.xsd **/*.json @@ -532,12 +620,23 @@ - + + + org.apache.maven.plugins + maven-dependency-plugin + + + com.google.auto.service:auto-service + + + org.apache.logging.log4j:log4j-core + + + @@ -549,6 +648,23 @@ org.cyclonedx cyclonedx-maven-plugin + + com.github.spotbugs + spotbugs-maven-plugin + ${spotbugs-maven-plugin.version} + + Max + Normal + true + + + + + check + + + + @@ -579,6 +695,11 @@ + + com.github.spotbugs + spotbugs-maven-plugin + ${spotbugs-maven-plugin.version} + diff --git a/spotbugs-exclude.xml b/spotbugs-exclude.xml new file mode 100644 index 00000000..d0a47383 --- /dev/null +++ b/spotbugs-exclude.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/gov/nist/secauto/oscal/lib/OscalBindingContext.java b/src/main/java/gov/nist/secauto/oscal/lib/OscalBindingContext.java index 98e8eba6..6768a756 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/OscalBindingContext.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/OscalBindingContext.java @@ -33,12 +33,11 @@ import gov.nist.secauto.oscal.lib.model.AssessmentResults; import gov.nist.secauto.oscal.lib.model.Catalog; import gov.nist.secauto.oscal.lib.model.ComponentDefinition; +import gov.nist.secauto.oscal.lib.model.MappingCollection; import gov.nist.secauto.oscal.lib.model.PlanOfActionAndMilestones; import gov.nist.secauto.oscal.lib.model.Profile; import gov.nist.secauto.oscal.lib.model.SystemSecurityPlan; -import org.jetbrains.annotations.NotNull; - import java.io.File; import java.io.IOException; import java.net.URISyntaxException; @@ -48,14 +47,16 @@ import javax.xml.namespace.QName; +import edu.umd.cs.findbugs.annotations.NonNull; + public class OscalBindingContext extends DefaultBindingContext { - @NotNull - private static final OscalBindingContext INSTANCE = new OscalBindingContext(); + @NonNull + private static final OscalBindingContext SINGLETON = new OscalBindingContext(); - @NotNull + @NonNull public static OscalBindingContext instance() { - return INSTANCE; + return SINGLETON; } /** @@ -64,7 +65,7 @@ public static OscalBindingContext instance() { * @param constraintSets * a set of additional constraints to apply */ - public OscalBindingContext(@NotNull Set<@NotNull IConstraintSet> constraintSets) { + public OscalBindingContext(@NonNull Set constraintSets) { super(constraintSets); registerBindingMatcher(new Matcher()); } @@ -76,115 +77,128 @@ protected OscalBindingContext() { registerBindingMatcher(new Matcher()); } - @NotNull - public Catalog loadCatalog(@NotNull URL url) throws IOException, URISyntaxException { + @NonNull + public Catalog loadCatalog(@NonNull URL url) throws IOException, URISyntaxException { return newBoundLoader().load(Catalog.class, url); } - @NotNull - public Catalog loadCatalog(@NotNull Path path) throws IOException { + @NonNull + public Catalog loadCatalog(@NonNull Path path) throws IOException { return newBoundLoader().load(Catalog.class, path); } - @SuppressWarnings("null") - @NotNull - public Catalog loadCatalog(@NotNull File file) throws IOException { - return loadCatalog(file.toPath()); + @NonNull + public Catalog loadCatalog(@NonNull File file) throws IOException { + return newBoundLoader().load(Catalog.class, file); } - @NotNull - public Profile loadProfile(@NotNull URL url) throws IOException, URISyntaxException { + @NonNull + public Profile loadProfile(@NonNull URL url) throws IOException, URISyntaxException { return newBoundLoader().load(Profile.class, url); } - @NotNull - public Profile loadProfile(@NotNull Path path) throws IOException { + @NonNull + public Profile loadProfile(@NonNull Path path) throws IOException { return newBoundLoader().load(Profile.class, path); } - @NotNull - public Profile loadProfile(@NotNull File file) throws IOException { + @NonNull + public Profile loadProfile(@NonNull File file) throws IOException { return newBoundLoader().load(Profile.class, file); } - @NotNull - public SystemSecurityPlan loadSystemSecurityPlan(@NotNull URL url) throws IOException, URISyntaxException { + @NonNull + public MappingCollection loadMapping(@NonNull URL url) throws IOException, URISyntaxException { + return newBoundLoader().load(MappingCollection.class, url); + } + + @NonNull + public MappingCollection loadMapping(@NonNull Path path) throws IOException { + return newBoundLoader().load(MappingCollection.class, path); + } + + @NonNull + public MappingCollection loadMapping(@NonNull File file) throws IOException { + return newBoundLoader().load(MappingCollection.class, file); + } + + @NonNull + public SystemSecurityPlan loadSystemSecurityPlan(@NonNull URL url) throws IOException, URISyntaxException { return newBoundLoader().load(SystemSecurityPlan.class, url); } - @NotNull - public SystemSecurityPlan loadSystemSecurityPlan(@NotNull Path path) throws IOException { + @NonNull + public SystemSecurityPlan loadSystemSecurityPlan(@NonNull Path path) throws IOException { return newBoundLoader().load(SystemSecurityPlan.class, path); } - @NotNull - public SystemSecurityPlan loadSystemSecurityPlan(@NotNull File file) throws IOException { + @NonNull + public SystemSecurityPlan loadSystemSecurityPlan(@NonNull File file) throws IOException { return newBoundLoader().load(SystemSecurityPlan.class, file); } - @NotNull - public ComponentDefinition loadComponentDefinition(@NotNull URL url) throws IOException, URISyntaxException { + @NonNull + public ComponentDefinition loadComponentDefinition(@NonNull URL url) throws IOException, URISyntaxException { return newBoundLoader().load(ComponentDefinition.class, url); } - @NotNull - public ComponentDefinition loadComponentDefinition(@NotNull Path path) throws IOException { + @NonNull + public ComponentDefinition loadComponentDefinition(@NonNull Path path) throws IOException { return newBoundLoader().load(ComponentDefinition.class, path); } - @NotNull - public ComponentDefinition loadComponentDefinition(@NotNull File file) throws IOException { + @NonNull + public ComponentDefinition loadComponentDefinition(@NonNull File file) throws IOException { return newBoundLoader().load(ComponentDefinition.class, file); } - @NotNull - public AssessmentPlan loadAssessmentPlan(@NotNull URL url) throws IOException, URISyntaxException { + @NonNull + public AssessmentPlan loadAssessmentPlan(@NonNull URL url) throws IOException, URISyntaxException { return newBoundLoader().load(AssessmentPlan.class, url); } - @NotNull - public AssessmentPlan loadAssessmentPlan(@NotNull Path path) throws IOException { + @NonNull + public AssessmentPlan loadAssessmentPlan(@NonNull Path path) throws IOException { return newBoundLoader().load(AssessmentPlan.class, path); } - @NotNull - public AssessmentPlan loadAssessmentPlan(@NotNull File file) throws IOException { + @NonNull + public AssessmentPlan loadAssessmentPlan(@NonNull File file) throws IOException { return newBoundLoader().load(AssessmentPlan.class, file); } - @NotNull - public AssessmentResults loadAssessmentResults(@NotNull URL url) throws IOException, URISyntaxException { + @NonNull + public AssessmentResults loadAssessmentResults(@NonNull URL url) throws IOException, URISyntaxException { return newBoundLoader().load(AssessmentResults.class, url); } - @NotNull - public AssessmentResults loadAssessmentResults(@NotNull Path path) throws IOException { + @NonNull + public AssessmentResults loadAssessmentResults(@NonNull Path path) throws IOException { return newBoundLoader().load(AssessmentResults.class, path); } - @NotNull - public AssessmentResults loadAssessmentResults(@NotNull File file) throws IOException { + @NonNull + public AssessmentResults loadAssessmentResults(@NonNull File file) throws IOException { return newBoundLoader().load(AssessmentResults.class, file); } - @NotNull - public PlanOfActionAndMilestones loadPlanOfActionAndMilestones(@NotNull URL url) + @NonNull + public PlanOfActionAndMilestones loadPlanOfActionAndMilestones(@NonNull URL url) throws IOException, URISyntaxException { return newBoundLoader().load(PlanOfActionAndMilestones.class, url); } - @NotNull - public PlanOfActionAndMilestones loadPlanOfActionAndMilestones(@NotNull Path path) throws IOException { + @NonNull + public PlanOfActionAndMilestones loadPlanOfActionAndMilestones(@NonNull Path path) throws IOException { return newBoundLoader().load(PlanOfActionAndMilestones.class, path); } - @NotNull - public PlanOfActionAndMilestones loadPlanOfActionAndMilestones(@NotNull File file) throws IOException { + @NonNull + public PlanOfActionAndMilestones loadPlanOfActionAndMilestones(@NonNull File file) throws IOException { return newBoundLoader().load(PlanOfActionAndMilestones.class, file); } - private class Matcher implements IBindingMatcher { - + private static class Matcher implements IBindingMatcher { @Override public Class getBoundClassForXmlQName(QName startElementQName) { Class clazz = null; @@ -196,6 +210,9 @@ public Class getBoundClassForXmlQName(QName startElementQName) { case "profile": clazz = Profile.class; break; + case "mapping-collection": + clazz = MappingCollection.class; + break; case "system-security-plan": clazz = SystemSecurityPlan.class; break; diff --git a/src/main/java/gov/nist/secauto/oscal/lib/OscalUtils.java b/src/main/java/gov/nist/secauto/oscal/lib/OscalUtils.java index fd2f0a55..69916bdb 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/OscalUtils.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/OscalUtils.java @@ -33,8 +33,7 @@ import gov.nist.secauto.oscal.lib.model.BackMatter.Resource.Base64; import gov.nist.secauto.oscal.lib.model.BackMatter.Resource.Rlink; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -47,6 +46,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import edu.umd.cs.findbugs.annotations.NonNull; + public final class OscalUtils { public static final String OSCAL_VERSION = "1.0.4"; private static final Pattern INTERNAL_REFERENCE_FRAGMENT_PATTERN = Pattern.compile("^#(.+)$"); @@ -55,9 +56,9 @@ private OscalUtils() { // disable construction } - public static boolean isInternalReference(@NotNull URI uri) { + public static boolean isInternalReference(@NonNull URI uri) { if (uri.isAbsolute()) { - return false; + return false; // NOPMD - readability } String schemeSpecificPart = uri.getSchemeSpecificPart(); @@ -65,14 +66,31 @@ public static boolean isInternalReference(@NotNull URI uri) { && uri.getFragment() != null; } - @SuppressWarnings("null") - @NotNull - public static String internalReferenceFragmentToId(@NotNull URI fragment) throws IllegalArgumentException { + /** + * Get the id based on a URI's fragment. + * + * @param fragment + * the URI to extract the identifier from + * @return the identifier + * @throws IllegalArgumentException + * if the fragment does not contain an identifier + */ + @NonNull + public static String internalReferenceFragmentToId(@NonNull URI fragment) { return internalReferenceFragmentToId(fragment.toString()); } - @NotNull - public static String internalReferenceFragmentToId(@NotNull String fragment) throws IllegalArgumentException { + /** + * Get the id based on a URI's fragment. + * + * @param fragment + * the URI to extract the identifier from + * @return the identifier + * @throws IllegalArgumentException + * if the fragment does not contain an identifier + */ + @NonNull + public static String internalReferenceFragmentToId(@NonNull String fragment) { Matcher matcher = INTERNAL_REFERENCE_FRAGMENT_PATTERN.matcher(fragment); String retval; if (matcher.matches()) { @@ -84,12 +102,12 @@ public static String internalReferenceFragmentToId(@NotNull String fragment) thr return retval; } - public static boolean hasBase64Data(@NotNull Resource resource) { + public static boolean hasBase64Data(@NonNull Resource resource) { return resource.getBase64() != null; } @Nullable - public static ByteBuffer getBase64Data(@NotNull Resource resource) { + public static ByteBuffer getBase64Data(@NonNull Resource resource) { Base64 base64 = resource.getBase64(); ByteBuffer retval = null; @@ -100,7 +118,7 @@ public static ByteBuffer getBase64Data(@NotNull Resource resource) { } @Nullable - public static URI getResourceURI(@NotNull Resource resource, @Nullable String preferredMediaType) { + public static URI getResourceURI(@NonNull Resource resource, @Nullable String preferredMediaType) { URI retval; if (hasBase64Data(resource)) { UUID uuid = resource.getUuid(); @@ -116,7 +134,7 @@ public static URI getResourceURI(@NotNull Resource resource, @Nullable String pr } @Nullable - public static Rlink findMatchingRLink(@NotNull Resource resource, @Nullable String preferredMediaType) { + public static Rlink findMatchingRLink(@NonNull Resource resource, @Nullable String preferredMediaType) { // find a suitable rlink reference List rlinks = resource.getRlinks(); @@ -136,7 +154,7 @@ public static Rlink findMatchingRLink(@NotNull Resource resource, @Nullable Stri } @Nullable - public static InputSource newInputSource(@NotNull Resource resource, @NotNull EntityResolver resolver, + public static InputSource newInputSource(@NonNull Resource resource, @NonNull EntityResolver resolver, @Nullable String preferredMediaType) throws IOException { URI uri = getResourceURI(resource, null); if (uri == null) { diff --git a/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/HasOscalNamespace.java b/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/HasOscalNamespace.java index 86d468f8..b6e5b140 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/HasOscalNamespace.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/HasOscalNamespace.java @@ -42,13 +42,13 @@ import gov.nist.secauto.oscal.lib.model.ControlPart; import gov.nist.secauto.oscal.lib.model.Property; -import org.jetbrains.annotations.NotNull; - import java.net.URI; import java.util.List; +import edu.umd.cs.findbugs.annotations.NonNull; + public final class HasOscalNamespace { - @NotNull + @NonNull static final IFunction SIGNATURE_ONE_ARG = IFunction.builder() .name("has-oscal-namespace") .argument(IArgument.newBuilder() @@ -56,6 +56,7 @@ public final class HasOscalNamespace { .type(IStringItem.class) .oneOrMore() .build()) + .allowUnboundedArity(true) .returnType(IBooleanItem.class) .focusDependent() .contextIndependent() @@ -64,7 +65,7 @@ public final class HasOscalNamespace { .functionHandler(HasOscalNamespace::executeOneArg) .build(); - @NotNull + @NonNull static final IFunction SIGNATURE_TWO_ARGS = IFunction.builder() .name("has-oscal-namespace") .argument(IArgument.newBuilder() @@ -77,6 +78,7 @@ public final class HasOscalNamespace { .type(IStringItem.class) .oneOrMore() .build()) + .allowUnboundedArity(true) .focusIndependent() .contextIndependent() .deterministic() @@ -89,45 +91,47 @@ private HasOscalNamespace() { // disable construction } - @NotNull - public static ISequence executeOneArg(@NotNull IFunction function, - @NotNull List<@NotNull ISequence> arguments, @NotNull DynamicContext dynamicContext, + @NonNull + public static ISequence executeOneArg(@NonNull IFunction function, + @NonNull List> arguments, @NonNull DynamicContext dynamicContext, INodeItem focus) { INodeItem node = focus; if (node == null) { - return ISequence.empty(); + return ISequence.empty(); // NOPMD - readability } ISequence namespaceArgs = FunctionUtils.asType(arguments.get(0)); if (namespaceArgs.isEmpty()) { - return ISequence.empty(); + return ISequence.empty(); // NOPMD - readability } return ISequence.of(hasNamespace(FunctionUtils.asType(node), namespaceArgs, dynamicContext)); } - @NotNull - public static ISequence executeTwoArg(@NotNull IFunction function, - @NotNull List<@NotNull ISequence> arguments, @NotNull DynamicContext dynamicContext, + @NonNull + public static ISequence executeTwoArg(@NonNull IFunction function, + @NonNull List> arguments, @NonNull DynamicContext dynamicContext, INodeItem focus) { ISequence nodeSequence = FunctionUtils.asType(arguments.get(0)); IItem node = FunctionUtils.getFirstItem(nodeSequence, true); if (node == null) { - return ISequence.empty(); + return ISequence.empty(); // NOPMD - readability } ISequence namespaceArgs = FunctionUtils.asType(arguments.get(1)); if (namespaceArgs.isEmpty()) { - return ISequence.empty(); + return ISequence.empty(); // NOPMD - readability } return ISequence.of(hasNamespace(FunctionUtils.asType(node), namespaceArgs, dynamicContext)); } - @NotNull - public static IBooleanItem hasNamespace(@NotNull IDefinitionNodeItem propOrPart, - @NotNull ISequence namespaces, @NotNull DynamicContext dynamicContext) + @NonNull + public static IBooleanItem hasNamespace( // NOPMD - item is boolean + @NonNull IDefinitionNodeItem propOrPart, + @NonNull ISequence namespaces, + @NonNull DynamicContext dynamicContext) throws MetapathException { Object propOrPartObject = propOrPart.getValue(); if (propOrPartObject == null) { diff --git a/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/OscalFunctionLibrary.java b/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/OscalFunctionLibrary.java index d207af2c..3303daa4 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/OscalFunctionLibrary.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/OscalFunctionLibrary.java @@ -26,10 +26,14 @@ package gov.nist.secauto.oscal.lib.metapath.function.library; -import gov.nist.secauto.metaschema.model.common.metapath.function.AbstractFunctionLibrary; +import com.google.auto.service.AutoService; +import gov.nist.secauto.metaschema.model.common.metapath.function.FunctionLibrary; +import gov.nist.secauto.metaschema.model.common.metapath.function.IFunctionLibrary; + +@AutoService(IFunctionLibrary.class) public class OscalFunctionLibrary - extends AbstractFunctionLibrary { + extends FunctionLibrary { public OscalFunctionLibrary() { registerFunction(ResolveProfile.SIGNATURE_NO_ARG); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/ResolveProfile.java b/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/ResolveProfile.java index fe8a82d3..aec61e28 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/ResolveProfile.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/metapath/function/library/ResolveProfile.java @@ -36,16 +36,17 @@ import gov.nist.secauto.metaschema.model.common.metapath.item.IItem; import gov.nist.secauto.metaschema.model.common.metapath.item.INodeItem; import gov.nist.secauto.oscal.lib.model.Catalog; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException; import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.io.IOException; import java.util.List; public final class ResolveProfile { - @NotNull + @NonNull static final IFunction SIGNATURE_NO_ARG = IFunction.builder() .name("resolve-profile") .returnType(INodeItem.class) @@ -56,7 +57,7 @@ public final class ResolveProfile { .functionHandler(ResolveProfile::executeNoArg) .build(); - @NotNull + @NonNull static final IFunction SIGNATURE_ONE_ARG = IFunction.builder() .name("resolve-profile") .argument(IArgument.newBuilder() @@ -76,35 +77,35 @@ private ResolveProfile() { // disable construction } - @NotNull - public static ISequence executeNoArg(@NotNull IFunction function, - @NotNull List<@NotNull ISequence> arguments, @NotNull DynamicContext dynamicContext, + @NonNull + public static ISequence executeNoArg(@NonNull IFunction function, + @NonNull List> arguments, @NonNull DynamicContext dynamicContext, INodeItem focus) { INodeItem item = focus; if (item == null) { - return ISequence.empty(); + return ISequence.empty(); // NOPMD - readability } return ISequence.of(resolveProfile(FunctionUtils.asType(item), dynamicContext)); } - @NotNull - public static ISequence executeOneArg(@NotNull IFunction function, - @NotNull List<@NotNull ISequence> arguments, @NotNull DynamicContext dynamicContext, + @NonNull + public static ISequence executeOneArg(@NonNull IFunction function, + @NonNull List> arguments, @NonNull DynamicContext dynamicContext, INodeItem focus) { ISequence arg = FunctionUtils.asType(arguments.get(0)); IItem item = FunctionUtils.getFirstItem(arg, true); if (item == null) { - return ISequence.empty(); + return ISequence.empty(); // NOPMD - readability } return ISequence.of(resolveProfile(FunctionUtils.asType(item), dynamicContext)); } - @NotNull - public static IDocumentNodeItem resolveProfile(@NotNull IDocumentNodeItem profile, - @NotNull DynamicContext dynamicContext) { + @NonNull + public static IDocumentNodeItem resolveProfile(@NonNull IDocumentNodeItem profile, + @NonNull DynamicContext dynamicContext) { Object profileObject = profile.getValue(); IDocumentNodeItem retval; @@ -116,7 +117,7 @@ public static IDocumentNodeItem resolveProfile(@NotNull IDocumentNodeItem profil resolver.setDynamicContext(dynamicContext); try { retval = resolver.resolve(profile); - } catch (IOException ex) { + } catch (IOException | ProfileResolutionException ex) { throw new MetapathException(String.format("Unable to resolve profile '%s'", profile.getBaseUri()), ex); } } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/AbstractOscalInstance.java b/src/main/java/gov/nist/secauto/oscal/lib/model/AbstractOscalInstance.java index 4248568a..d7b86f5a 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/AbstractOscalInstance.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/AbstractOscalInstance.java @@ -29,14 +29,14 @@ import gov.nist.secauto.oscal.lib.model.BackMatter.Resource; import gov.nist.secauto.oscal.lib.model.metadata.IBackMatter; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.UUID; public abstract class AbstractOscalInstance implements IOscalInstance { @Override - public Resource getResourceByUuid(@NotNull UUID uuid) { + public Resource getResourceByUuid(@NonNull UUID uuid) { IBackMatter backMatter = getBackMatter(); Resource retval = null; diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/IOscalInstance.java b/src/main/java/gov/nist/secauto/oscal/lib/model/IOscalInstance.java index 687321e5..1322e756 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/IOscalInstance.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/IOscalInstance.java @@ -28,7 +28,7 @@ import gov.nist.secauto.oscal.lib.model.BackMatter.Resource; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.UUID; @@ -39,5 +39,5 @@ public interface IOscalInstance { BackMatter getBackMatter(); - Resource getResourceByUuid(@NotNull UUID id); + Resource getResourceByUuid(@NonNull UUID id); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractParameter.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractParameter.java index a40c2b2a..89990856 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractParameter.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractParameter.java @@ -37,8 +37,6 @@ import gov.nist.secauto.oscal.lib.model.Property; import gov.nist.secauto.oscal.lib.model.metadata.IProperty; -import org.jetbrains.annotations.NotNull; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -47,10 +45,12 @@ import java.util.Objects; import java.util.stream.Stream; +import edu.umd.cs.findbugs.annotations.NonNull; + public abstract class AbstractParameter implements IParameter { @Override - public Stream<@NotNull String> getParameterReferences() { + public Stream getParameterReferences() { // handle prop name="aggregates" Stream aggregatesIds = CollectionUtil.listOrEmpty(getProps()).stream() @@ -71,112 +71,112 @@ public abstract class AbstractParameter implements IParameter { .map(insert -> insert.getIdReference().toStringOrNull())); } @SuppressWarnings("null") - Stream<@NotNull String> retval = Stream.concat(aggregatesIds, selectInsertIds) + Stream retval = Stream.concat(aggregatesIds, selectInsertIds) .filter(Objects::nonNull) .distinct(); return retval; } - @NotNull - public static Builder builder(@NotNull String id) { + @NonNull + public static Builder builder(@NonNull String id) { return new Builder(id); } public static class Builder { - @NotNull + @NonNull private final String id; - private String clazz; + private String clazz; // NOPMD - intentional private final List props = new LinkedList<>(); private final List links = new LinkedList<>(); - private MarkupLine label; - private MarkupMultiline usage; + private MarkupLine label; // NOPMD - intentional + private MarkupMultiline usage; // NOPMD - intentional private final List constraints = new LinkedList<>(); private final List guidelines = new LinkedList<>(); - private List values = new LinkedList<>(); + private List values = new LinkedList<>(); // NOPMD - intentional private ParameterSelection selection; - private MarkupMultiline remarks; + private MarkupMultiline remarks; // NOPMD - intentional - @SuppressWarnings("null") - public Builder(@NotNull String id) { - this.id = Objects.requireNonNull(id, "id"); + public Builder(@NonNull String id) { + this.id = Objects.requireNonNull(id); } - @SuppressWarnings("null") - @NotNull - public Builder clazz(@NotNull String value) { - this.clazz = Objects.requireNonNull(value, "value"); + @NonNull + public Builder clazz(@NonNull String value) { + this.clazz = Objects.requireNonNull(value); return this; } - @NotNull - public Builder prop(@NotNull Property value) { - this.props.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder prop(@NonNull Property value) { + this.props.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder link(@NotNull Link value) { - this.links.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder link(@NonNull Link value) { + this.links.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder label(@NotNull String markdown) { - return label(MarkupLine.fromMarkdown(Objects.requireNonNull(markdown, "markdown"))); + @NonNull + public Builder label(@NonNull String markdown) { + return label(MarkupLine.fromMarkdown(Objects.requireNonNull(markdown))); } - @SuppressWarnings("null") - @NotNull - public Builder label(@NotNull MarkupLine value) { - this.label = Objects.requireNonNull(value, "value"); + @NonNull + public Builder label(@NonNull MarkupLine value) { + this.label = Objects.requireNonNull(value); return this; } - @NotNull - public Builder usage(@NotNull String markdown) { - return usage(MarkupMultiline.fromMarkdown(Objects.requireNonNull(markdown, "markdown"))); + @NonNull + public Builder usage(@NonNull String markdown) { + return usage(MarkupMultiline.fromMarkdown(Objects.requireNonNull(markdown))); } - @SuppressWarnings("null") - @NotNull - public Builder usage(@NotNull MarkupMultiline value) { - this.usage = Objects.requireNonNull(value, "value"); + @NonNull + public Builder usage(@NonNull MarkupMultiline value) { + this.usage = Objects.requireNonNull(value); return this; } - @NotNull - public Builder constraint(@NotNull ParameterConstraint value) { - this.constraints.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder constraint(@NonNull ParameterConstraint value) { + this.constraints.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder guideline(@NotNull ParameterGuideline value) { - this.guidelines.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder guideline(@NonNull ParameterGuideline value) { + this.guidelines.add(Objects.requireNonNull(value)); return this; } - @SuppressWarnings("null") - @NotNull - public Builder values(@NotNull String... values) { + @NonNull + public Builder values(@NonNull String... values) { return values(Arrays.asList(values)); } - @NotNull - public Builder values(@NotNull Collection values) { + @NonNull + public Builder values(@NonNull Collection values) { this.values = new ArrayList<>(values); return this; } - @SuppressWarnings("null") - @NotNull - public Builder select(@NotNull ParameterSelection value) { - this.selection = Objects.requireNonNull(value, "value"); + @NonNull + public Builder select(@NonNull ParameterSelection value) { + this.selection = Objects.requireNonNull(value); + return this; + } + + @NonNull + public Builder remarks(@NonNull MarkupMultiline value) { + this.remarks = Objects.requireNonNull(value); return this; } - @NotNull + @NonNull public Parameter build() { Parameter retval = new Parameter(); retval.setId(id); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractPart.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractPart.java index a3137f5f..661be72c 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractPart.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/AbstractPart.java @@ -34,7 +34,7 @@ import gov.nist.secauto.oscal.lib.model.Link; import gov.nist.secauto.oscal.lib.model.Property; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.net.URI; import java.util.LinkedList; @@ -45,13 +45,12 @@ public abstract class AbstractPart implements IPart { - @SuppressWarnings({ "null" }) @Override - @NotNull - public Stream getInserts(@NotNull Predicate filter) { + @NonNull + public Stream getInserts(@NonNull Predicate filter) { MarkupMultiline prose = getProse(); - @NotNull + @NonNull Stream retval; if (prose == null) { retval = Stream.empty(); @@ -62,99 +61,93 @@ public Stream getInserts(@NotNull Predicate return retval; } - public Stream<@NotNull IPart> getPartsRecursively() { + public Stream getPartsRecursively() { return Stream.concat( Stream.of(this), CollectionUtil.listOrEmpty(getParts()).stream() .flatMap(part -> part.getPartsRecursively())); } - @NotNull - public static Builder builder(@NotNull String name) { + @NonNull + public static Builder builder(@NonNull String name) { return new Builder(name); } public static class Builder { - private String id; - @NotNull + private String id; // NOPMD - intentional + @NonNull private final String name; - private URI namespace; - private String clazz; - private MarkupMultiline prose; - private MarkupLine title; + private URI namespace; // NOPMD - intentional + private String clazz; // NOPMD - intentional + private MarkupMultiline prose; // NOPMD - intentional + private MarkupLine title; // NOPMD - intentional private final List props = new LinkedList<>(); private final List links = new LinkedList<>(); private final List parts = new LinkedList<>(); - @SuppressWarnings("null") - public Builder(@NotNull String name) { - this.name = Objects.requireNonNull(name, "name"); + public Builder(@NonNull String name) { + this.name = Objects.requireNonNull(name); } - @SuppressWarnings("null") - @NotNull - public Builder id(@NotNull String value) { - this.id = Objects.requireNonNull(value, "value"); + @NonNull + public Builder id(@NonNull String value) { // NOPMD - intentional + this.id = Objects.requireNonNull(value); return this; } - @SuppressWarnings("null") - @NotNull - public Builder namespace(@NotNull URI value) { - this.namespace = Objects.requireNonNull(value, "value"); + @NonNull + public Builder namespace(@NonNull URI value) { + this.namespace = Objects.requireNonNull(value); return this; } - @SuppressWarnings("null") - @NotNull - public Builder clazz(@NotNull String value) { - this.clazz = Objects.requireNonNull(value, "value"); + @NonNull + public Builder clazz(@NonNull String value) { + this.clazz = Objects.requireNonNull(value); return this; } - @NotNull - public Builder title(@NotNull String markdown) { - return title(MarkupLine.fromMarkdown(Objects.requireNonNull(markdown, "markdown"))); + @NonNull + public Builder title(@NonNull String markdown) { + return title(MarkupLine.fromMarkdown(Objects.requireNonNull(markdown))); } - @SuppressWarnings("null") - @NotNull - public Builder title(@NotNull MarkupLine value) { - this.title = Objects.requireNonNull(value, "value"); + @NonNull + public Builder title(@NonNull MarkupLine value) { + this.title = Objects.requireNonNull(value); return this; } - @NotNull - public Builder prose(@NotNull String markdown) { - return prose(MarkupMultiline.fromMarkdown(Objects.requireNonNull(markdown, "markdown"))); + @NonNull + public Builder prose(@NonNull String markdown) { + return prose(MarkupMultiline.fromMarkdown(Objects.requireNonNull(markdown))); } - @SuppressWarnings("null") - @NotNull - public Builder prose(@NotNull MarkupMultiline value) { - this.prose = Objects.requireNonNull(value, "value"); + @NonNull + public Builder prose(@NonNull MarkupMultiline value) { + this.prose = Objects.requireNonNull(value); return this; } - @NotNull - public Builder prop(@NotNull Property value) { - this.props.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder prop(@NonNull Property value) { + this.props.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder link(@NotNull Link value) { - this.links.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder link(@NonNull Link value) { + this.links.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder part(@NotNull ControlPart value) { - this.parts.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder part(@NonNull ControlPart value) { + this.parts.add(Objects.requireNonNull(value)); return this; } - @NotNull + @NonNull public ControlPart build() { ControlPart retval = new ControlPart(); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/IParameter.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/IParameter.java index 3f325a25..9f3f6318 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/IParameter.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/IParameter.java @@ -29,8 +29,6 @@ import gov.nist.secauto.oscal.lib.model.ParameterSelection; import gov.nist.secauto.oscal.lib.model.Property; -import org.jetbrains.annotations.NotNull; - import java.util.List; import java.util.stream.Stream; @@ -39,5 +37,5 @@ public interface IParameter { ParameterSelection getSelect(); - Stream<@NotNull String> getParameterReferences(); + Stream getParameterReferences(); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/IPart.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/IPart.java index 76ade7b7..6cbe3b86 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/IPart.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/IPart.java @@ -30,7 +30,7 @@ import gov.nist.secauto.metaschema.model.common.datatype.markup.flexmark.InsertAnchorNode; import gov.nist.secauto.oscal.lib.model.ControlPart; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; import java.util.function.Predicate; @@ -41,6 +41,6 @@ public interface IPart { List getParts(); - @NotNull - Stream getInserts(@NotNull Predicate filter); + @NonNull + Stream getInserts(@NonNull Predicate filter); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalog.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalog.java index f3930ed4..94077406 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalog.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalog.java @@ -30,7 +30,7 @@ import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; import gov.nist.secauto.oscal.lib.model.AbstractOscalInstance; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.stream.Stream; @@ -38,10 +38,9 @@ public abstract class AbstractCatalog extends AbstractOscalInstance implements ICatalog { - @SuppressWarnings("null") - @NotNull + @NonNull @Override - public Stream<@NotNull String> getReferencedParameterIds() { + public Stream getReferencedParameterIds() { // get parameters referenced by the control's parameters return CollectionUtil.listOrEmpty(getParams()).stream() .flatMap(ObjectUtils::filterNull) diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogGroup.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogGroup.java index 430b3efb..3911394a 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogGroup.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogGroup.java @@ -36,7 +36,7 @@ import gov.nist.secauto.oscal.lib.model.Parameter; import gov.nist.secauto.oscal.lib.model.Property; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.LinkedList; import java.util.List; @@ -47,13 +47,12 @@ public abstract class AbstractCatalogGroup implements ICatalogGroup, IGroupContainer { - @SuppressWarnings("null") - @NotNull + @NonNull @Override - public Stream<@NotNull String> getReferencedParameterIds() { + public Stream getReferencedParameterIds() { // get parameters referenced by the group's parts - Stream<@NotNull String> insertIds = CollectionUtil.listOrEmpty(getParts()).stream() + Stream insertIds = CollectionUtil.listOrEmpty(getParts()).stream() // Get the full part hierarchy .flatMap(part -> Stream.concat(Stream.of(part), part.getPartsRecursively())) // Get the inserts for each part @@ -63,7 +62,7 @@ public abstract class AbstractCatalogGroup .flatMap(ObjectUtils::filterNull); // get parameters referenced by the control's parameters - Stream<@NotNull String> parameterIds = CollectionUtil.listOrEmpty(getParams()).stream() + Stream parameterIds = CollectionUtil.listOrEmpty(getParams()).stream() .flatMap(ObjectUtils::filterNull) .flatMap(param -> param.getParameterReferences()); @@ -71,13 +70,13 @@ public abstract class AbstractCatalogGroup .distinct(); } - @NotNull - public static Builder builder(@NotNull String id) { + @NonNull + public static Builder builder(@NonNull String id) { return new Builder(id); } public static class Builder { - @NotNull + @NonNull private final String id; private String clazz; @@ -89,68 +88,66 @@ public static class Builder { private final List groups = new LinkedList<>(); private final List controls = new LinkedList<>(); - @SuppressWarnings("null") - public Builder(@NotNull String id) { + public Builder(@NonNull String id) { this.id = Objects.requireNonNull(id, "id"); } - @SuppressWarnings("null") - @NotNull - public Builder clazz(@NotNull String value) { + @NonNull + public Builder clazz(@NonNull String value) { this.clazz = Objects.requireNonNull(value, "value"); return this; } - @NotNull - public Builder title(@NotNull String markdown) { + @NonNull + public Builder title(@NonNull String markdown) { this.title = MarkupLine.fromMarkdown(Objects.requireNonNull(markdown, "markdown")); return this; } @SuppressWarnings("null") - @NotNull - public Builder title(@NotNull MarkupLine value) { + @NonNull + public Builder title(@NonNull MarkupLine value) { this.title = Objects.requireNonNull(value, "value"); return this; } - @NotNull - public Builder param(@NotNull Parameter value) { + @NonNull + public Builder param(@NonNull Parameter value) { this.params.add(Objects.requireNonNull(value, "value")); return this; } - @NotNull - public Builder prop(@NotNull Property value) { + @NonNull + public Builder prop(@NonNull Property value) { this.props.add(Objects.requireNonNull(value, "value")); return this; } - @NotNull - public Builder link(@NotNull Link value) { + @NonNull + public Builder link(@NonNull Link value) { this.links.add(Objects.requireNonNull(value, "value")); return this; } - @NotNull - public Builder part(@NotNull ControlPart value) { + @NonNull + public Builder part(@NonNull ControlPart value) { this.parts.add(Objects.requireNonNull(value, "value")); return this; } - @NotNull - public Builder group(@NotNull CatalogGroup value) { + @NonNull + public Builder group(@NonNull CatalogGroup value) { this.groups.add(Objects.requireNonNull(value, "value")); return this; } - @NotNull - public Builder control(@NotNull Control value) { + @NonNull + public Builder control(@NonNull Control value) { this.controls.add(Objects.requireNonNull(value, "value")); return this; } - @NotNull + @NonNull public CatalogGroup build() { CatalogGroup retval = new CatalogGroup(); retval.setId(id); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogVisitor.java index 00ddd220..5183775c 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractCatalogVisitor.java @@ -26,12 +26,13 @@ package gov.nist.secauto.oscal.lib.model.control.catalog; +import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; import gov.nist.secauto.oscal.lib.model.Catalog; import gov.nist.secauto.oscal.lib.model.CatalogGroup; import gov.nist.secauto.oscal.lib.model.Control; import gov.nist.secauto.oscal.lib.model.Parameter; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Objects; @@ -43,48 +44,45 @@ protected RESULT aggregateResult(RESULT previous, RESULT current) { return current; } - @SuppressWarnings("null") @Override public RESULT visitCatalog(Catalog catalog, CONTEXT context) { - RESULT result = catalog.getGroups().stream() + RESULT result = CollectionUtil.listOrEmpty(catalog.getGroups()).stream() .filter(Objects::nonNull) .map(childGroup -> visitGroup(childGroup, context)) .reduce(defaultResult(), (previous, current) -> aggregateResult(previous, current)); - result = catalog.getControls().stream() + result = CollectionUtil.listOrEmpty(catalog.getControls()).stream() .filter(Objects::nonNull) .map(childControl -> visitControl(childControl, context)) .reduce(result, (previous, current) -> aggregateResult(previous, current)); - return catalog.getParams().stream() + return CollectionUtil.listOrEmpty(catalog.getParams()).stream() .filter(Objects::nonNull) .map(childParameter -> visitParameter(childParameter, context)) .reduce(result, (previous, current) -> aggregateResult(previous, current)); } - @SuppressWarnings("null") @Override - public RESULT visitGroup(@NotNull CatalogGroup group, CONTEXT context) { - RESULT result = group.getGroups().stream() + public RESULT visitGroup(@NonNull CatalogGroup group, CONTEXT context) { + RESULT result = CollectionUtil.listOrEmpty(group.getGroups()).stream() .filter(Objects::nonNull) .map(childGroup -> visitGroup(childGroup, context)) .reduce(defaultResult(), (previous, current) -> aggregateResult(previous, current)); - result = group.getControls().stream() + result = CollectionUtil.listOrEmpty(group.getControls()).stream() .filter(Objects::nonNull) .map(childControl -> visitControl(childControl, context)) .reduce(result, (previous, current) -> aggregateResult(previous, current)); - return group.getParams().stream() + return CollectionUtil.listOrEmpty(group.getParams()).stream() .filter(Objects::nonNull) .map(childParameter -> visitParameter(childParameter, context)) .reduce(result, (previous, current) -> aggregateResult(previous, current)); } - @SuppressWarnings("null") @Override public RESULT visitControl(Control control, CONTEXT context) { - RESULT result = control.getControls().stream() + RESULT result = CollectionUtil.listOrEmpty(control.getControls()).stream() .filter(Objects::nonNull) .map(childControl -> visitControl(childControl, context)) .reduce(defaultResult(), (previous, current) -> aggregateResult(previous, current)); - return control.getParams().stream() + return CollectionUtil.listOrEmpty(control.getParams()).stream() .filter(Objects::nonNull) .map(childParameter -> visitParameter(childParameter, context)) .reduce(result, (previous, current) -> aggregateResult(previous, current)); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractControl.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractControl.java index 377cdbbf..cea4a7e7 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractControl.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/AbstractControl.java @@ -36,7 +36,7 @@ import gov.nist.secauto.oscal.lib.model.Parameter; import gov.nist.secauto.oscal.lib.model.Property; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.LinkedList; import java.util.List; @@ -70,13 +70,12 @@ public void afterDeserialize(Object parent) { } } - @SuppressWarnings("null") - @NotNull + @NonNull @Override - public Stream<@NotNull String> getReferencedParameterIds() { + public Stream getReferencedParameterIds() { // get parameters referenced by the group's parts - Stream<@NotNull String> insertIds = CollectionUtil.listOrEmpty(getParts()).stream() + Stream insertIds = CollectionUtil.listOrEmpty(getParts()).stream() // Get the full part hierarchy .flatMap(part -> Stream.concat(Stream.of(part), part.getPartsRecursively())) // Get the inserts for each part @@ -86,7 +85,7 @@ public void afterDeserialize(Object parent) { .flatMap(ObjectUtils::filterNull); // get parameters referenced by the control's parameters - Stream<@NotNull String> parameterIds = CollectionUtil.listOrEmpty(getParams()).stream() + Stream parameterIds = CollectionUtil.listOrEmpty(getParams()).stream() .flatMap(ObjectUtils::filterNull) .flatMap(param -> param.getParameterReferences()); @@ -94,80 +93,76 @@ public void afterDeserialize(Object parent) { .distinct(); } - @NotNull - public static Builder builder(@NotNull String id) { + @NonNull + public static Builder builder(@NonNull String id) { return new Builder(id); } public static class Builder { - @NotNull + @NonNull private final String id; - private String clazz; - private MarkupLine title; + private String clazz; // NOPMD - intentional + private MarkupLine title; // NOPMD - intentional private final List params = new LinkedList<>(); private final List props = new LinkedList<>(); private final List links = new LinkedList<>(); private final List parts = new LinkedList<>(); private final List controls = new LinkedList<>(); - @SuppressWarnings("null") - public Builder(@NotNull String id) { + public Builder(@NonNull String id) { this.id = Objects.requireNonNull(id, "id"); } - @SuppressWarnings("null") - @NotNull - public Builder clazz(@NotNull String value) { - this.clazz = Objects.requireNonNull(value, "value"); + @NonNull + public Builder clazz(@NonNull String value) { + this.clazz = Objects.requireNonNull(value); return this; } - @SuppressWarnings("null") - @NotNull - public Builder title(@NotNull String markdown) { - this.title = MarkupLine.fromMarkdown(Objects.requireNonNull(markdown, "markdown")); + @NonNull + public Builder title(@NonNull String markdown) { + this.title = MarkupLine.fromMarkdown(Objects.requireNonNull(markdown)); return this; } - @SuppressWarnings("null") - @NotNull - public Builder title(@NotNull MarkupLine value) { - this.title = Objects.requireNonNull(value, "value"); + @NonNull + public Builder title(@NonNull MarkupLine value) { + this.title = Objects.requireNonNull(value); return this; } - @NotNull - public Builder param(@NotNull Parameter value) { - this.params.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder param(@NonNull Parameter value) { + this.params.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder prop(@NotNull Property value) { - this.props.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder prop(@NonNull Property value) { + this.props.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder link(@NotNull Link value) { - this.links.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder link(@NonNull Link value) { + this.links.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder part(@NotNull ControlPart value) { - this.parts.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder part(@NonNull ControlPart value) { + this.parts.add(Objects.requireNonNull(value)); return this; } - @NotNull - public Builder control(@NotNull Control value) { - this.controls.add(Objects.requireNonNull(value, "value")); + @NonNull + public Builder control(@NonNull Control value) { + this.controls.add(Objects.requireNonNull(value)); return this; } - @NotNull + @NonNull public Control build() { Control retval = new Control(); retval.setId(id); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalog.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalog.java index 0f55d0b0..c417e9df 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalog.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalog.java @@ -27,5 +27,5 @@ package gov.nist.secauto.oscal.lib.model.control.catalog; public interface ICatalog extends IGroupContainer { - + // no additional methods } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalogVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalogVisitor.java index 47970765..bc5356f8 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalogVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/ICatalogVisitor.java @@ -31,14 +31,14 @@ import gov.nist.secauto.oscal.lib.model.Control; import gov.nist.secauto.oscal.lib.model.Parameter; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; public interface ICatalogVisitor { - RESULT visitCatalog(@NotNull Catalog catalog, CONTEXT context); + RESULT visitCatalog(@NonNull Catalog catalog, CONTEXT context); - RESULT visitGroup(@NotNull CatalogGroup group, CONTEXT context); + RESULT visitGroup(@NonNull CatalogGroup group, CONTEXT context); - RESULT visitControl(@NotNull Control control, CONTEXT context); + RESULT visitControl(@NonNull Control control, CONTEXT context); - RESULT visitParameter(@NotNull Parameter parameter, CONTEXT context); + RESULT visitParameter(@NonNull Parameter parameter, CONTEXT context); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IControlContainer.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IControlContainer.java index ab0df865..12596361 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IControlContainer.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IControlContainer.java @@ -29,14 +29,14 @@ import gov.nist.secauto.oscal.lib.model.Control; import gov.nist.secauto.oscal.lib.model.Parameter; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; import java.util.stream.Stream; public interface IControlContainer { - List<@NotNull Control> getControls(); + List getControls(); /** * Add a new {@link Control} item to the end of the underlying collection. @@ -45,7 +45,7 @@ public interface IControlContainer { * the item to add * @return {@code true} */ - boolean addControl(@NotNull Control item); + boolean addControl(@NonNull Control item); /** * Remove the first matching {@link Control} item from the underlying collection. @@ -54,9 +54,9 @@ public interface IControlContainer { * the item to remove * @return {@code true} if the item was removed or {@code false} otherwise */ - boolean removeControl(@NotNull Control item); + boolean removeControl(@NonNull Control item); - List<@NotNull Parameter> getParams(); + List getParams(); /** * Add a new {@link Parameter} item to the underlying collection. @@ -65,7 +65,7 @@ public interface IControlContainer { * the item to add * @return {@code true} */ - boolean addParam(@NotNull Parameter item); + boolean addParam(@NonNull Parameter item); /** * Remove the first matching {@link Parameter} item from the underlying collection. @@ -74,12 +74,12 @@ public interface IControlContainer { * the item to remove * @return {@code true} if the item was removed or {@code false} otherwise */ - boolean removeParam(@NotNull Parameter item); + boolean removeParam(@NonNull Parameter item); /** * Get the parameter identifiers referenced in the object's context, but not by their child objects. * * @return a stream of identifiers */ - Stream<@NotNull String> getReferencedParameterIds(); + Stream getReferencedParameterIds(); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IGroupContainer.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IGroupContainer.java index ca855ab3..fad06ed6 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IGroupContainer.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/catalog/IGroupContainer.java @@ -28,13 +28,13 @@ import gov.nist.secauto.oscal.lib.model.CatalogGroup; -import org.jetbrains.annotations.NotNull; - import java.util.List; +import edu.umd.cs.findbugs.annotations.NonNull; + public interface IGroupContainer extends IControlContainer { - List<@NotNull CatalogGroup> getGroups(); + List getGroups(); /** * Add a new {@link CatalogGroup} item to the end of the underlying collection. @@ -43,7 +43,7 @@ public interface IGroupContainer extends IControlContainer { * the item to add * @return {@code true} */ - boolean addGroup(@NotNull CatalogGroup item); + boolean addGroup(@NonNull CatalogGroup item); /** * Remove the first matching {@link CatalogGroup} item from the underlying collection. @@ -52,5 +52,5 @@ public interface IGroupContainer extends IControlContainer { * the item to remove * @return {@code true} if the item was removed or {@code false} otherwise */ - boolean removeGroup(@NotNull CatalogGroup item); + boolean removeGroup(@NonNull CatalogGroup item); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/control/profile/AbstractProfileSelectControlById.java b/src/main/java/gov/nist/secauto/oscal/lib/model/control/profile/AbstractProfileSelectControlById.java index 057b8816..0e62fac9 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/control/profile/AbstractProfileSelectControlById.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/control/profile/AbstractProfileSelectControlById.java @@ -29,7 +29,7 @@ import gov.nist.secauto.oscal.lib.model.ProfileSelectControlById; import gov.nist.secauto.oscal.lib.model.ProfileSelectControlById.Matching; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collection; import java.util.LinkedList; @@ -41,7 +41,7 @@ public abstract class AbstractProfileSelectControlById implements IProfileSelectControlById { // TODO: move implementation from profile resolver selection code here - @NotNull + @NonNull public static Builder builder() { return new Builder(); } @@ -51,31 +51,31 @@ public static class Builder { private final List withIds = new LinkedList<>(); private final List matching = new LinkedList<>(); - @NotNull + @NonNull public Builder withChildControls(boolean value) { this.withChildControls = value; return this; } - @NotNull - public Builder withId(@NotNull String id) { + @NonNull + public Builder withId(@NonNull String id) { withIds.add(id); return this; } - @NotNull - public Builder withIds(@NotNull Collection ids) { + @NonNull + public Builder withIds(@NonNull Collection ids) { withIds.addAll(ids); return this; } - @NotNull - public Builder matching(@NotNull Pattern pattern) { + @NonNull + public Builder matching(@NonNull Pattern pattern) { matching.add(pattern); return this; } - @NotNull + @NonNull public ProfileSelectControlById build() { ProfileSelectControlById retval = new ProfileSelectControlById(); retval.setWithChildControls(withChildControls ? "yes" : "no"); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractBackMatter.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractBackMatter.java index 452fe38f..83a91eaf 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractBackMatter.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractBackMatter.java @@ -28,16 +28,16 @@ import gov.nist.secauto.oscal.lib.model.BackMatter.Resource; -import org.jetbrains.annotations.NotNull; - import java.util.List; import java.util.UUID; +import edu.umd.cs.findbugs.annotations.NonNull; + public abstract class AbstractBackMatter implements IBackMatter { @Override - public Resource getResourceByUuid(@NotNull UUID uuid) { - List<@NotNull Resource> resources = getResources(); + public Resource getResourceByUuid(@NonNull UUID uuid) { + List resources = getResources(); Resource retval = null; if (resources != null) { diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractLink.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractLink.java index 6511b069..e7838492 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractLink.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractLink.java @@ -29,52 +29,61 @@ import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupLine; import gov.nist.secauto.oscal.lib.model.Link; -import org.jetbrains.annotations.NotNull; - import java.net.URI; +import java.util.LinkedList; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; public abstract class AbstractLink implements ILink { - @NotNull - public static Builder builder(@NotNull URI href) { + public static List merge(@NonNull List original, @NonNull List additional) { + return Stream.concat(original.stream(), additional.stream()) + .collect(Collectors.toCollection(LinkedList::new)); + } + + @NonNull + public static Builder builder(@NonNull URI href) { return new Builder(href); } public static class Builder { - @NotNull + @NonNull private final URI href; private String rel; private String mediaType; private MarkupLine text; @SuppressWarnings("null") - public Builder(@NotNull URI href) { + public Builder(@NonNull URI href) { this.href = Objects.requireNonNull(href, "href"); } @SuppressWarnings("null") - @NotNull - public Builder relation(@NotNull String relation) { + @NonNull + public Builder relation(@NonNull String relation) { this.rel = Objects.requireNonNull(relation, "rel"); return this; } @SuppressWarnings("null") - @NotNull - public Builder value(@NotNull String mediaType) { + @NonNull + public Builder value(@NonNull String mediaType) { this.mediaType = Objects.requireNonNull(mediaType, "mediaType"); return this; } @SuppressWarnings("null") - @NotNull - public Builder clazz(@NotNull MarkupLine text) { + @NonNull + public Builder clazz(@NonNull MarkupLine text) { this.text = Objects.requireNonNull(text, "text"); return this; } - @NotNull + @NonNull public Link build() { Link retval = new Link(); retval.setHref(href); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractMetadata.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractMetadata.java index cd6cbf4a..4c122b85 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractMetadata.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractMetadata.java @@ -28,16 +28,16 @@ import gov.nist.secauto.oscal.lib.model.Party; -import org.jetbrains.annotations.NotNull; - import java.util.List; import java.util.UUID; +import edu.umd.cs.findbugs.annotations.NonNull; + public abstract class AbstractMetadata implements IMetadata { @Override - public Party getPartyByUuid(@NotNull UUID uuid) { - List<@NotNull Party> parties = getParties(); + public Party getPartyByUuid(@NonNull UUID uuid) { + List parties = getParties(); Party retval = null; if (parties != null) { diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractProperty.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractProperty.java index 3d65a302..905f52df 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractProperty.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/AbstractProperty.java @@ -29,29 +29,32 @@ import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; import gov.nist.secauto.oscal.lib.model.Property; -import org.jetbrains.annotations.NotNull; - import java.net.URI; +import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.xml.namespace.QName; +import edu.umd.cs.findbugs.annotations.NonNull; + public abstract class AbstractProperty implements IProperty { - @NotNull - public static QName qname(URI namespace, @NotNull String name) { + @NonNull + public static QName qname(URI namespace, @NonNull String name) { return new QName(normalizeNamespace(namespace).toString(), name); } - @NotNull - public static QName qname(@NotNull String name) { + @NonNull + public static QName qname(@NonNull String name) { return new QName(OSCAL_NAMESPACE.toString(), name); } - @NotNull + @NonNull public static URI normalizeNamespace(URI namespace) { URI propertyNamespace = namespace; if (propertyNamespace == null) { @@ -61,8 +64,8 @@ public static URI normalizeNamespace(URI namespace) { } @SuppressWarnings("null") - @NotNull - public static Optional find(List props, @NotNull QName qname) { + @NonNull + public static Optional find(List props, @NonNull QName qname) { return CollectionUtil.listOrEmpty(props).stream().filter(prop -> qname.equals(prop.getQName())).findFirst(); } @@ -70,68 +73,68 @@ protected AbstractProperty() { // only concrete classes should construct } + public static List merge(@NonNull List original, @NonNull List additional) { + return Stream.concat(original.stream(), additional.stream()) + .collect(Collectors.toCollection(LinkedList::new)); + } + @Override - public boolean isNamespaceEqual(@NotNull URI namespace) { + public boolean isNamespaceEqual(@NonNull URI namespace) { return normalizeNamespace(getNs()).equals(namespace); } - @NotNull + @NonNull public QName getQName() { return new QName(normalizeNamespace(getNs()).toString(), getName()); } - @NotNull - public static Builder builder(@NotNull String name) { + @NonNull + public static Builder builder(@NonNull String name) { return new Builder(name); } public static class Builder { - @NotNull + @NonNull private final String name; - private UUID uuid; - private URI namespace; - private String value; - private String clazz; + private UUID uuid; // NOPMD - intentional + private URI namespace; // NOPMD - intentional + private String value; // NOPMD - intentional + private String clazz; // NOPMD - intentional - @SuppressWarnings("null") - public Builder(@NotNull String name) { + public Builder(@NonNull String name) { this.name = Objects.requireNonNull(name, "name"); } - @SuppressWarnings("null") - @NotNull - public Builder uuid(@NotNull UUID uuid) { - this.uuid = Objects.requireNonNull(uuid, "uuid"); + @NonNull + public Builder uuid(@NonNull UUID uuid) { + this.uuid = Objects.requireNonNull(uuid); return this; } - @SuppressWarnings("null") - @NotNull - public Builder namespace(@NotNull URI namespace) { + @NonNull + public Builder namespace(@NonNull URI namespace) { if (IProperty.OSCAL_NAMESPACE.equals(namespace)) { - this.namespace = null; + this.namespace = null; // NOPMD - ok } else { - this.namespace = Objects.requireNonNull(namespace, "namespace"); + this.namespace = Objects.requireNonNull(namespace); } return this; } - @SuppressWarnings("null") - @NotNull - public Builder value(@NotNull String value) { - this.value = Objects.requireNonNull(value, "value"); + @NonNull + public Builder value(@NonNull String value) { + this.value = Objects.requireNonNull(value); return this; } - @SuppressWarnings("null") - @NotNull - public Builder clazz(@NotNull String clazz) { - this.clazz = Objects.requireNonNull(clazz, "clazz"); + @NonNull + public Builder clazz(@NonNull String clazz) { + this.clazz = Objects.requireNonNull(clazz); return this; } - @NotNull + @NonNull public Property build() { Property retval = new Property(); retval.setName(name); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IBackMatter.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IBackMatter.java index 4abe7957..916400f8 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IBackMatter.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IBackMatter.java @@ -28,15 +28,16 @@ import gov.nist.secauto.oscal.lib.model.BackMatter.Resource; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; import java.util.UUID; +import edu.umd.cs.findbugs.annotations.NonNull; + public interface IBackMatter { - List<@NotNull Resource> getResources(); + List getResources(); @Nullable - Resource getResourceByUuid(@NotNull UUID uuid); + Resource getResourceByUuid(@NonNull UUID uuid); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/ILink.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/ILink.java index af1b4c55..803299f8 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/ILink.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/ILink.java @@ -27,5 +27,5 @@ package gov.nist.secauto.oscal.lib.model.metadata; public interface ILink { - + // no additional methods } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IMetadata.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IMetadata.java index 3c87de0d..48490889 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IMetadata.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IMetadata.java @@ -30,19 +30,19 @@ import gov.nist.secauto.oscal.lib.model.Party; import gov.nist.secauto.oscal.lib.model.Role; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; import java.util.List; import java.util.UUID; public interface IMetadata { @Nullable - Party getPartyByUuid(@NotNull UUID uuid); + Party getPartyByUuid(@NonNull UUID uuid); - List<@NotNull Role> getRoles(); + List getRoles(); - List<@NotNull Location> getLocations(); + List getLocations(); - List<@NotNull Party> getParties(); + List getParties(); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IProperty.java b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IProperty.java index f2ad6d21..b8ac173a 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IProperty.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/model/metadata/IProperty.java @@ -26,21 +26,21 @@ package gov.nist.secauto.oscal.lib.model.metadata; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.net.URI; public interface IProperty { @SuppressWarnings("null") - @NotNull + @NonNull URI OSCAL_NAMESPACE = URI.create("http://csrc.nist.gov/ns/oscal"); @SuppressWarnings("null") - @NotNull + @NonNull URI RMF_NAMESPACE = URI.create("http://csrc.nist.gov/ns/rmf"); String getName(); URI getNs(); - boolean isNamespaceEqual(@NotNull URI namespace); + boolean isNamespaceEqual(@NonNull URI namespace); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/AbstractCatalogControlItemVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/AbstractCatalogControlItemVisitor.java index 1130a435..29688c8c 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/AbstractCatalogControlItemVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/AbstractCatalogControlItemVisitor.java @@ -29,7 +29,7 @@ import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; public abstract class AbstractCatalogControlItemVisitor { @@ -37,11 +37,11 @@ public abstract class AbstractCatalogControlItemVisitor { protected abstract R aggregateResults(R first, R second, T context); - protected R visitCatalog(@NotNull IDocumentNodeItem profileDocument, T context) { + protected R visitCatalog(@NonNull IDocumentNodeItem profileDocument, T context) { return visitGroupContainer(profileDocument.getRootAssemblyNodeItem(), context); } - protected R visitGroupContainer(@NotNull IRequiredValueModelNodeItem catalogOrGroup, T context) { + protected R visitGroupContainer(@NonNull IRequiredValueModelNodeItem catalogOrGroup, T context) { R groupResult = catalogOrGroup.getModelItemsByName("group").stream() .map(groupItem -> { return visitGroup(groupItem, context); @@ -52,7 +52,7 @@ protected R visitGroupContainer(@NotNull IRequiredValueModelNodeItem catalogOrGr return aggregateResults(groupResult, controlResult, context); } - protected R visitControlContainer(@NotNull IRequiredValueModelNodeItem catalogOrGroupOrControl, T context) { + protected R visitControlContainer(@NonNull IRequiredValueModelNodeItem catalogOrGroupOrControl, T context) { return catalogOrGroupOrControl.getModelItemsByName("control").stream() .map(controlItem -> { return visitControl(controlItem, context); @@ -60,11 +60,11 @@ protected R visitControlContainer(@NotNull IRequiredValueModelNodeItem catalogOr .reduce(newDefaultResult(context), (first, second) -> aggregateResults(first, second, context)); } - protected R visitGroup(@NotNull IRequiredValueModelNodeItem groupItem, T context) { + protected R visitGroup(@NonNull IRequiredValueModelNodeItem groupItem, T context) { return visitGroupContainer(groupItem, context); } - protected R visitControl(@NotNull IRequiredValueModelNodeItem controlItem, T context) { + protected R visitControl(@NonNull IRequiredValueModelNodeItem controlItem, T context) { return visitControlContainer(controlItem, context); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ControlIndexingVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ControlIndexingVisitor.java new file mode 100644 index 00000000..03d79f05 --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ControlIndexingVisitor.java @@ -0,0 +1,83 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver; + +import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; +import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class ControlIndexingVisitor + extends AbstractCatalogControlItemVisitor { + @NonNull + private final IIndexer indexer; + + public ControlIndexingVisitor(@NonNull IIdentifierMapper mapper) { + this.indexer = new DefaultIndexer(mapper); + } + + @NonNull + protected IIndexer getIndexer() { + return indexer; + } + + @NonNull + public Index getIndex() { + return indexer.getIndex(); + } + + @Override + protected Void visitCatalog(@NonNull IDocumentNodeItem catalogItem, Void context) { + return super.visitCatalog(catalogItem, null); + } + + @Override + protected Void visitControl(@NonNull IRequiredValueModelNodeItem controlItem, Void context) { + getIndexer().addControl(controlItem, true); + return super.visitControl(controlItem, context); + } + + @Override + protected Void visitControlContainer(@NonNull IRequiredValueModelNodeItem catalogOrGroupOrControl, + Void context) { + // handle parameters + catalogOrGroupOrControl.getModelItemsByName("param").forEach(paramItem -> { + getIndexer().addParameter(paramItem); + }); + return super.visitControlContainer(catalogOrGroupOrControl, null); + } + + @Override + protected Void newDefaultResult(Void context) { + return null; + } + + @Override + protected Void aggregateResults(Void first, Void second, Void context) { + return null; + } +} diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ControlSelectionVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ControlSelectionVisitor.java index 96097b50..15bc6fed 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ControlSelectionVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ControlSelectionVisitor.java @@ -36,35 +36,36 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; + +import edu.umd.cs.findbugs.annotations.NonNull; public class ControlSelectionVisitor - extends AbstractCatalogControlItemVisitor { + extends AbstractCatalogControlItemVisitor { private static final Logger LOGGER = LogManager.getLogger(ControlSelectionVisitor.class); - @NotNull + @NonNull private static final MetapathExpression BACK_MATTER_RESOURCES_METAPATH = MetapathExpression.compile("back-matter/resource"); - @NotNull + @NonNull private static final MetapathExpression PART_METAPATH = MetapathExpression.compile("part|part//part"); - @NotNull + @NonNull private final IControlFilter filter; - @NotNull + @NonNull private final IIndexer indexer; - public ControlSelectionVisitor(@NotNull IControlFilter filter, @NotNull IIdentifierMapper mapper) { + public ControlSelectionVisitor(@NonNull IControlFilter filter, @NonNull IIdentifierMapper mapper) { this.filter = filter; this.indexer = new DefaultIndexer(mapper); } - @NotNull + @NonNull protected IIndexer getIndexer() { return indexer; } - @NotNull + @NonNull public Index getIndex() { return indexer.getIndex(); } @@ -79,13 +80,13 @@ protected Boolean aggregateResults(Boolean first, Boolean second, Boolean defaul return first || second; } - public void visitProfile(@NotNull IDocumentNodeItem profileItem) { + public void visitProfile(@NonNull IDocumentNodeItem profileItem) { IRootAssemblyNodeItem root = profileItem.getRootAssemblyNodeItem(); indexMetadata(root); indexBackMatter(root); } - public void visitCatalog(@NotNull IDocumentNodeItem catalogItem) { + public void visitCatalog(@NonNull IDocumentNodeItem catalogItem) { visitCatalog(catalogItem, false); IRootAssemblyNodeItem root = catalogItem.getRootAssemblyNodeItem(); @@ -94,7 +95,7 @@ public void visitCatalog(@NotNull IDocumentNodeItem catalogItem) { } @Override - protected Boolean visitGroup(@NotNull IRequiredValueModelNodeItem groupItem, Boolean defaultMatch) { + protected Boolean visitGroup(@NonNull IRequiredValueModelNodeItem groupItem, Boolean defaultMatch) { boolean result = super.visitGroup(groupItem, defaultMatch); CatalogGroup group = (CatalogGroup) groupItem.getValue(); @@ -107,11 +108,11 @@ protected Boolean visitGroup(@NotNull IRequiredValueModelNodeItem groupItem, Boo } @Override - protected Boolean visitControl(@NotNull IRequiredValueModelNodeItem controlItem, Boolean defaultMatch) { + protected Boolean visitControl(@NonNull IRequiredValueModelNodeItem controlItem, Boolean defaultMatch) { Control control = (Control) controlItem.getValue(); // determine if the control is a match - Pair<@NotNull Boolean, @NotNull Boolean> matchResult = filter.match(control, defaultMatch); + Pair matchResult = filter.match(control, defaultMatch); @SuppressWarnings("null") boolean isMatch = matchResult.getLeft(); @SuppressWarnings("null") @@ -125,11 +126,11 @@ protected Boolean visitControl(@NotNull IRequiredValueModelNodeItem controlItem, boolean defaultChildMatch = isMatch && isWithChildren; - return aggregateResults(isMatch, super.visitControl(controlItem, defaultChildMatch), null); + return aggregateResults(isMatch, super.visitControl(controlItem, defaultChildMatch), defaultMatch); } @Override - protected Boolean visitControlContainer(@NotNull IRequiredValueModelNodeItem catalogOrGroupOrControl, + protected Boolean visitControlContainer(@NonNull IRequiredValueModelNodeItem catalogOrGroupOrControl, Boolean defaultMatch) { boolean retval = super.visitControlContainer(catalogOrGroupOrControl, defaultMatch); @@ -140,14 +141,14 @@ protected Boolean visitControlContainer(@NotNull IRequiredValueModelNodeItem cat // handle parts PART_METAPATH.evaluate(catalogOrGroupOrControl).asStream() - .map(item -> (@NotNull IRequiredValueModelNodeItem) item) + .map(item -> (IRequiredValueModelNodeItem) item) .forEachOrdered(partItem -> { indexer.addPart(partItem); }); return retval; } - protected void indexMetadata(@NotNull IRootAssemblyNodeItem rootItem) { + protected void indexMetadata(@NonNull IRootAssemblyNodeItem rootItem) { IIndexer indexer = getIndexer(); rootItem.getModelItemsByName("metadata").forEach(metadataItem -> { @@ -165,10 +166,10 @@ protected void indexMetadata(@NotNull IRootAssemblyNodeItem rootItem) { }); } - protected void indexBackMatter(@NotNull IRootAssemblyNodeItem rootItem) { + protected void indexBackMatter(@NonNull IRootAssemblyNodeItem rootItem) { IIndexer indexer = getIndexer(); BACK_MATTER_RESOURCES_METAPATH.evaluate(rootItem).asStream() - .map(item -> (@NotNull IRequiredValueModelNodeItem) item) + .map(item -> (IRequiredValueModelNodeItem) item) .forEachOrdered(resourceItem -> { indexer.addResource(resourceItem); }); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilter.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilter.java index e527e266..7f06dcca 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilter.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilter.java @@ -34,7 +34,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.List; @@ -43,11 +42,13 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import edu.umd.cs.findbugs.annotations.NonNull; + public class DefaultControlSelectionFilter implements IControlSelectionFilter { private static final Logger LOGGER = LogManager.getLogger(DefaultControlSelectionFilter.class); - @NotNull - private final List<@NotNull Selection> selections; + @NonNull + private final List selections; /** * Construct a new selection filter based on the provided list of select criteria. @@ -56,7 +57,7 @@ public class DefaultControlSelectionFilter implements IControlSelectionFilter { * a list of select criteria */ @SuppressWarnings("null") - public DefaultControlSelectionFilter(@NotNull List selections) { + public DefaultControlSelectionFilter(@NonNull List selections) { this.selections = selections.stream() // ignore null entries .filter(Objects::nonNull) @@ -65,12 +66,12 @@ public DefaultControlSelectionFilter(@NotNull List apply(@NotNull IControl control) { + public Pair apply(IControl control) { String id = control.getId(); if (id == null) { - throw new IllegalArgumentException("control is missing an identifier"); + throw new ProfileResolutionEvaluationException("control is missing an identifier"); } return match(id); } @@ -85,15 +86,15 @@ public DefaultControlSelectionFilter(@NotNull List match(String id) { + @NonNull + protected Pair match(String id) { return selections.parallelStream() .map(selection -> selection.match(id)) // filter out non-matches .filter(pair -> pair.getLeft()) // aggregate matches .reduce((first, second) -> { - Pair<@NotNull Boolean, @NotNull Boolean> result; + Pair result; if (first.getLeft() || second.getLeft()) { // at least one matches boolean withChild = first.getLeft() && first.getRight() || second.getLeft() && second.getRight(); @@ -106,11 +107,8 @@ public DefaultControlSelectionFilter(@NotNull List (char) ch.intValue()).map(ch -> { String value; @@ -146,7 +144,7 @@ private static Pattern toPattern(@NotNull ProfileSelectControlById.Matching matc return Pattern.compile(regex); } - private class Selection { + private static class Selection { private final boolean withChildControls; private final Set identifiers; @@ -181,8 +179,8 @@ public boolean isWithChildControls() { return withChildControls; } - @NotNull - protected Pair<@NotNull Boolean, @NotNull Boolean> match(String id) { + @NonNull + protected Pair match(String id) { // first check for direct match boolean result = identifiers.stream().anyMatch(controlIdentifier -> controlIdentifier.equals(id)); if (!result) { diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultIndexer.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultIndexer.java index f6c5f0fc..3f600604 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultIndexer.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultIndexer.java @@ -38,38 +38,41 @@ import gov.nist.secauto.oscal.lib.model.Role; import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.UUID; public class DefaultIndexer implements IIndexer { - @NotNull + @NonNull private final Index index; - @NotNull + @NonNull private final IIdentifierMapper mapper; - public DefaultIndexer(@NotNull IIdentifierMapper mapper) { + public DefaultIndexer(@NonNull IIdentifierMapper mapper) { this(new Index(), mapper); } - public DefaultIndexer(@NotNull Index index, @NotNull IIdentifierMapper mapper) { + @SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "intending to store this parameter") + public DefaultIndexer(@NonNull Index index, @NonNull IIdentifierMapper mapper) { this.index = index; this.mapper = mapper; } @Override - @NotNull + @NonNull + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to expose this field") public Index getIndex() { return index; } - @NotNull + @NonNull protected IIdentifierMapper getMapper() { return mapper; } @Override - public EntityItem addRole(@NotNull IRequiredValueModelNodeItem item) { + public EntityItem addRole(@NonNull IRequiredValueModelNodeItem item) { Role role = (Role) item.getValue(); String identifier = ObjectUtils.notNull(role.getId()); String mappedIdentifier = getMapper().mapRoleIdentifier(identifier); @@ -84,7 +87,7 @@ public EntityItem addRole(@NotNull IRequiredValueModelNodeItem item) { } @Override - public EntityItem addLocation(@NotNull IRequiredValueModelNodeItem item) { + public EntityItem addLocation(@NonNull IRequiredValueModelNodeItem item) { Location location = (Location) item.getValue(); UUID identifier = ObjectUtils.notNull(location.getUuid()); @@ -95,7 +98,7 @@ public EntityItem addLocation(@NotNull IRequiredValueModelNodeItem item) { } @Override - public EntityItem addParty(@NotNull IRequiredValueModelNodeItem item) { + public EntityItem addParty(@NonNull IRequiredValueModelNodeItem item) { Party party = (Party) item.getValue(); UUID identifier = ObjectUtils.notNull(party.getUuid()); @@ -106,7 +109,7 @@ public EntityItem addParty(@NotNull IRequiredValueModelNodeItem item) { } @Override - public EntityItem addGroup(@NotNull IRequiredValueModelNodeItem item, boolean selected) { + public EntityItem addGroup(@NonNull IRequiredValueModelNodeItem item, boolean selected) { CatalogGroup group = (CatalogGroup) item.getValue(); if (selected) { @@ -131,7 +134,7 @@ public EntityItem addGroup(@NotNull IRequiredValueModelNodeItem item, boolean se } @Override - public EntityItem addControl(@NotNull IRequiredValueModelNodeItem item, boolean selected) { + public EntityItem addControl(@NonNull IRequiredValueModelNodeItem item, boolean selected) { Control control = (Control) item.getValue(); String identifier = ObjectUtils.notNull(control.getId()); String mappedIdentifier = getMapper().mapControlIdentifier(identifier); @@ -150,7 +153,7 @@ public EntityItem addControl(@NotNull IRequiredValueModelNodeItem item, boolean } @Override - public EntityItem addParameter(@NotNull IRequiredValueModelNodeItem item) { + public EntityItem addParameter(@NonNull IRequiredValueModelNodeItem item) { Parameter parameter = (Parameter) item.getValue(); String identifier = ObjectUtils.notNull(parameter.getId()); String mappedIdentifier = getMapper().mapParameterIdentifier(identifier); @@ -165,7 +168,7 @@ public EntityItem addParameter(@NotNull IRequiredValueModelNodeItem item) { } @Override - public EntityItem addPart(@NotNull IRequiredValueModelNodeItem item) { + public EntityItem addPart(@NonNull IRequiredValueModelNodeItem item) { ControlPart part = (ControlPart) item.getValue(); String identifier = part.getId(); @@ -186,7 +189,7 @@ public EntityItem addPart(@NotNull IRequiredValueModelNodeItem item) { } @Override - public EntityItem addResource(@NotNull IRequiredValueModelNodeItem item) { + public EntityItem addResource(@NonNull IRequiredValueModelNodeItem item) { Resource resource = (Resource) item.getValue(); UUID identifier = ObjectUtils.notNull(resource.getUuid()); @@ -196,39 +199,35 @@ public EntityItem addResource(@NotNull IRequiredValueModelNodeItem item) { identifier); } - protected EntityItem addItem(@NotNull ItemType type, @NotNull IRequiredValueModelNodeItem item, - @NotNull UUID identifier) { + protected EntityItem addItem(@NonNull ItemType type, @NonNull IRequiredValueModelNodeItem item, + @NonNull UUID identifier) { EntityItem.Builder builder = EntityItem.builder() - .instance(item, identifier) + .instance(item, type, identifier) .source(ObjectUtils.requireNonNull(item.getBaseUri(), "item must have an associated URI")); return addItem(type, builder); } - protected EntityItem addItem(@NotNull ItemType type, @NotNull IRequiredValueModelNodeItem item, - @NotNull String identifier) { + protected EntityItem addItem(@NonNull ItemType type, @NonNull IRequiredValueModelNodeItem item, + @NonNull String identifier) { EntityItem.Builder builder = EntityItem.builder() - .instance(item, identifier) + .instance(item, type, identifier) .source(ObjectUtils.requireNonNull(item.getBaseUri(), "item must have an associated URI")); return addItem(type, builder); } - protected EntityItem addItem(@NotNull ItemType type, @NotNull IRequiredValueModelNodeItem item, - @NotNull String identifier, - @NotNull String mappedIdentifier) { + protected EntityItem addItem(@NonNull ItemType type, @NonNull IRequiredValueModelNodeItem item, + @NonNull String identifier, + @NonNull String mappedIdentifier) { EntityItem.Builder builder = EntityItem.builder() - .instance(item, mappedIdentifier) + .instance(item, type, mappedIdentifier) .originalIdentifier(identifier) .source(ObjectUtils.requireNonNull(item.getBaseUri(), "item must have an associated URI")); return addItem(type, builder); } - protected EntityItem addItem(@NotNull ItemType type, @NotNull EntityItem.Builder builder) { - - builder - .itemType(type); - + protected EntityItem addItem(@NonNull ItemType type, @NonNull EntityItem.Builder builder) { return getIndex().addItem(builder.build()); } } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultResult.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultResult.java index ba547f56..25575904 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultResult.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultResult.java @@ -34,22 +34,24 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + public class DefaultResult implements IResult { private static final Logger LOGGER = LogManager.getLogger(DefaultResult.class); - @NotNull - private final Set<@NotNull Parameter> promotedParameters; - @NotNull - private final Set<@NotNull Control> promotedControls; - @NotNull - private final Set<@NotNull String> requiredParameterIds; + @NonNull + private final Set promotedParameters; + @NonNull + private final Set promotedControls; + @NonNull + private final Set requiredParameterIds; public DefaultResult() { this.promotedParameters = new LinkedHashSet<>(); @@ -58,35 +60,37 @@ public DefaultResult() { } @Override - @NotNull - public Collection<@NotNull Parameter> getPromotedParameters() { + @NonNull + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to expose this field") + public Collection getPromotedParameters() { return promotedParameters; } @Override - @NotNull - public Collection<@NotNull Control> getPromotedControls() { + @NonNull + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to expose this field") + public Collection getPromotedControls() { return promotedControls; } @Override - @NotNull - public Set<@NotNull String> getRequiredParameterIds() { + @NonNull + public Set getRequiredParameterIds() { return CollectionUtil.unmodifiableSet(requiredParameterIds); } @Override - public void requireParameters(@NotNull Set<@NotNull String> requiredParameterIds) { + public void requireParameters(@NonNull Set requiredParameterIds) { this.requiredParameterIds.addAll(requiredParameterIds); } @Override - public boolean isParameterRequired(@NotNull String id) { + public boolean isParameterRequired(@NonNull String id) { return getRequiredParameterIds().contains(id); } @Override - public void promoteParameter(@NotNull Parameter param) { + public void promoteParameter(@NonNull Parameter param) { if (LOGGER.isDebugEnabled()) { LOGGER.atDebug().log("promoting parameter '{}'", param.getId()); } @@ -94,7 +98,7 @@ public void promoteParameter(@NotNull Parameter param) { } @Override - public void promoteControl(@NotNull Control control) { + public void promoteControl(@NonNull Control control) { if (LOGGER.isDebugEnabled()) { LOGGER.atDebug().log("promoting control '{}'", control.getId()); } @@ -102,7 +106,7 @@ public void promoteControl(@NotNull Control control) { } @Override - public void applyTo(@NotNull Catalog parent) { + public void applyTo(@NonNull Catalog parent) { getPromotedParameters().forEach(param -> parent.addParam(param)); getPromotedControls().forEach(control -> { parent.addControl(control); @@ -111,7 +115,7 @@ public void applyTo(@NotNull Catalog parent) { } @Override - public void applyTo(@NotNull CatalogGroup parent) { + public void applyTo(@NonNull CatalogGroup parent) { getPromotedParameters().forEach(param -> parent.addParam(param)); getPromotedControls().forEach(control -> { parent.addControl(control); @@ -120,7 +124,7 @@ public void applyTo(@NotNull CatalogGroup parent) { } @Override - public void applyTo(@NotNull Control parent) { + public void applyTo(@NonNull Control parent) { getPromotedParameters().forEach(param -> parent.addParam(param)); getPromotedControls().forEach(control -> { parent.addControl(control); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/EntityItem.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/EntityItem.java index 0a7261cc..06191f9e 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/EntityItem.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/EntityItem.java @@ -26,6 +26,7 @@ package gov.nist.secauto.oscal.lib.profile.resolver; +import gov.nist.secauto.metaschema.model.common.datatype.adapter.UuidAdapter; import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression; import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression.ResultType; import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; @@ -35,12 +36,13 @@ import gov.nist.secauto.oscal.lib.model.control.catalog.IControlContainer; import gov.nist.secauto.oscal.lib.profile.resolver.policy.IReferenceVisitor; -import org.jetbrains.annotations.NotNull; - import java.net.URI; +import java.util.Locale; import java.util.Objects; import java.util.UUID; +import edu.umd.cs.findbugs.annotations.NonNull; + public final class EntityItem { private static final MetapathExpression CONTAINER_METAPATH = MetapathExpression.compile("(ancestor::control|ancestor::group)[1])"); @@ -56,15 +58,15 @@ public enum ItemType { RESOURCE; } - @NotNull + @NonNull private final String originalIdentifier; - @NotNull + @NonNull private final String identifier; - @NotNull + @NonNull private final IRequiredValueModelNodeItem instance; - @NotNull + @NonNull private final ItemType itemType; - @NotNull + @NonNull private final URI source; private int referenceCount; // 0 by default private boolean resolved; // false by default @@ -73,8 +75,7 @@ public static Builder builder() { return new Builder(); } - @SuppressWarnings("null") - private EntityItem(@NotNull Builder builder) { + private EntityItem(@NonNull Builder builder) { this.originalIdentifier = builder.originalIdentifier == null ? builder.identifier : builder.originalIdentifier; this.identifier = Objects.requireNonNull(builder.identifier, "identifier"); this.instance = Objects.requireNonNull(builder.instance, "instance"); @@ -82,33 +83,33 @@ private EntityItem(@NotNull Builder builder) { this.source = Objects.requireNonNull(builder.source, "source"); } - @NotNull + @NonNull public String getOriginalIdentifier() { return originalIdentifier; } - @NotNull + @NonNull public String getIdentifier() { return identifier; } - @NotNull + @NonNull public IRequiredValueModelNodeItem getInstance() { return instance; } - @NotNull + @NonNull @SuppressWarnings("unchecked") public T getInstanceValue() { return (T) instance.getValue(); } - @NotNull + @NonNull public ItemType getItemType() { return itemType; } - @NotNull + @NonNull public URI getSource() { return source; } @@ -133,7 +134,7 @@ public void incrementReferenceCount() { referenceCount += 1; } - public boolean isSelected(@NotNull Index index) { + public boolean isSelected(@NonNull Index index) { boolean retval; switch (getItemType()) { case CONTROL: @@ -159,12 +160,12 @@ public boolean isSelected(@NotNull Index index) { retval = true; break; default: - throw new IllegalStateException(getItemType().name()); + throw new UnsupportedOperationException(getItemType().name()); } return retval; } - public void accept(@NotNull IReferenceVisitor visitor) { + public void accept(@NonNull IReferenceVisitor visitor) { IRequiredValueModelNodeItem instance = getInstance(); switch (getItemType()) { case CONTROL: @@ -192,44 +193,61 @@ public void accept(@NotNull IReferenceVisitor visitor) { visitor.visitRole(instance); break; default: - throw new IllegalStateException(getItemType().name()); + throw new UnsupportedOperationException(getItemType().name()); + } + } + + @NonNull + public static String normalizeIdentifier(@NonNull ItemType type, @NonNull String identifier) { + String retval = identifier; + switch (type) { + case LOCATION: + case PARTY: + case RESOURCE: + if (UuidAdapter.UUID_PATTERN.matches(identifier)) { + // normalize the UUID + retval = identifier.toLowerCase(Locale.ROOT); + } + break; + default: + // do nothing + break; } + + return retval; } public static class Builder { - private String originalIdentifier; + private String originalIdentifier; // NOPMD - builder method private String identifier; - private IRequiredValueModelNodeItem instance; + private IRequiredValueModelNodeItem instance; // NOPMD - builder method private ItemType itemType; - private URI source; + private URI source; // NOPMD - builder method - public Builder instance(@NotNull IRequiredValueModelNodeItem item, @NotNull UUID identifier) { - return instance(item, ObjectUtils.notNull(identifier.toString())); + public Builder instance(@NonNull IRequiredValueModelNodeItem item, @NonNull ItemType itemType, + @NonNull UUID identifier) { + return instance(item, itemType, ObjectUtils.notNull(identifier.toString())); } - @SuppressWarnings("null") - public Builder instance(@NotNull IRequiredValueModelNodeItem item, @NotNull String identifier) { - this.identifier = Objects.requireNonNull(identifier, "identifier"); + public Builder instance(@NonNull IRequiredValueModelNodeItem item, @NonNull ItemType itemType, + @NonNull String identifier) { + this.identifier = normalizeIdentifier(itemType, ObjectUtils.requireNonNull(identifier, "identifier")); this.instance = Objects.requireNonNull(item, "item"); + this.itemType = itemType; return this; } - public Builder originalIdentifier(@NotNull String identifier) { - this.originalIdentifier = identifier; - return this; - } - - public Builder itemType(@NotNull ItemType itemType) { - this.itemType = itemType; + public Builder originalIdentifier(@NonNull String identifier) { + this.originalIdentifier = normalizeIdentifier(itemType, identifier); return this; } - public Builder source(@NotNull URI source) { + public Builder source(@NonNull URI source) { this.source = source; return this; } - @NotNull + @NonNull public EntityItem build() { return new EntityItem(this); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FilterNonSelectedVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FilterNonSelectedVisitor.java index f11cebda..041533f1 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FilterNonSelectedVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FilterNonSelectedVisitor.java @@ -46,24 +46,27 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; public class FilterNonSelectedVisitor { private static final Logger LOGGER = LogManager.getLogger(FilterNonSelectedVisitor.class); - @NotNull + @NonNull private final Index index; - public FilterNonSelectedVisitor(@NotNull Index index) { + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to store this parameter") + public FilterNonSelectedVisitor(@NonNull Index index) { this.index = index; } - @NotNull + @NonNull protected Index getIndex() { return index; } - public void visitCatalog(@NotNull IDocumentNodeItem catalogItem) { + public void visitCatalog(@NonNull IDocumentNodeItem catalogItem) { IRootAssemblyNodeItem root = catalogItem.getRootAssemblyNodeItem(); Catalog catalog = (Catalog) catalogItem.getValue(); @@ -154,7 +157,7 @@ private void visitBackMatter(IRequiredValueModelNodeItem child) { }); } - @NotNull + @NonNull protected IResult visitGroup(IRequiredValueModelNodeItem item, IGroupContainer container) { CatalogGroup group = (CatalogGroup) item.getValue(); @@ -206,7 +209,7 @@ protected IResult visitGroup(IRequiredValueModelNodeItem item, IGroupContainer c return retval; } - @NotNull + @NonNull protected IResult visitControl(IRequiredValueModelNodeItem item, IControlContainer container) { Control control = (Control) item.getValue(); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FlatStructureCatalogVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FlatStructureCatalogVisitor.java index 0741b7f6..02eeb851 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FlatStructureCatalogVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/FlatStructureCatalogVisitor.java @@ -32,19 +32,19 @@ import gov.nist.secauto.oscal.lib.model.Control; import gov.nist.secauto.oscal.lib.model.Parameter; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Iterator; import java.util.List; public class FlatStructureCatalogVisitor { - public void visitCatalog(@NotNull Catalog catalog) { + public void visitCatalog(@NonNull Catalog catalog) { DefaultResult result = new DefaultResult(); // process children for (Iterator iter = CollectionUtil.listOrEmpty(catalog.getGroups()).iterator(); iter.hasNext();) { @SuppressWarnings("null") - @NotNull + @NonNull CatalogGroup child = iter.next(); DefaultResult childResult = visitGroup(child); @@ -55,7 +55,7 @@ public void visitCatalog(@NotNull Catalog catalog) { for (Iterator iter = CollectionUtil.listOrEmpty(catalog.getControls()).iterator(); iter.hasNext();) { @SuppressWarnings("null") - @NotNull + @NonNull Control child = iter.next(); DefaultResult childResult = visitControl(child); @@ -65,15 +65,15 @@ public void visitCatalog(@NotNull Catalog catalog) { result.applyTo(catalog); } - @NotNull - public DefaultResult visitGroup(@NotNull CatalogGroup group) { + @NonNull + public DefaultResult visitGroup(@NonNull CatalogGroup group) { // process children DefaultResult retval = new DefaultResult(); // groups for (Iterator iter = CollectionUtil.listOrEmpty(group.getGroups()).iterator(); iter.hasNext();) { @SuppressWarnings("null") - @NotNull + @NonNull CatalogGroup child = iter.next(); DefaultResult result = visitGroup(child); retval.append(result); @@ -81,14 +81,14 @@ public DefaultResult visitGroup(@NotNull CatalogGroup group) { for (Iterator iter = CollectionUtil.listOrEmpty(group.getParams()).iterator(); iter.hasNext();) { @SuppressWarnings("null") - @NotNull + @NonNull Parameter child = iter.next(); retval.promoteParameter(child); } for (Iterator iter = CollectionUtil.listOrEmpty(group.getControls()).iterator(); iter.hasNext();) { @SuppressWarnings("null") - @NotNull + @NonNull Control child = iter.next(); retval.promoteControl(child); DefaultResult result = visitControl(child); @@ -98,8 +98,8 @@ public DefaultResult visitGroup(@NotNull CatalogGroup group) { return retval; } - @NotNull - public DefaultResult visitControl(@NotNull Control control) { + @NonNull + public DefaultResult visitControl(@NonNull Control control) { DefaultResult result = new DefaultResult(); List controlChildren = CollectionUtil.listOrEmpty(control.getControls()); @@ -107,7 +107,7 @@ public DefaultResult visitControl(@NotNull Control control) { for (Iterator iter = CollectionUtil.listOrEmpty(control.getControls()).iterator(); iter.hasNext();) { @SuppressWarnings("null") - @NotNull + @NonNull Control child = iter.next(); DefaultResult childResult = visitControl(child); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlFilter.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlFilter.java index d2ef466a..62eba3e5 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlFilter.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlFilter.java @@ -33,44 +33,45 @@ import gov.nist.secauto.oscal.lib.model.control.profile.IProfileSelectControlById; import org.apache.commons.lang3.tuple.Pair; -import org.jetbrains.annotations.NotNull; import java.util.List; +import edu.umd.cs.findbugs.annotations.NonNull; + public interface IControlFilter { - @NotNull + @NonNull IControlFilter ALWAYS_MATCH = new IControlFilter() { @Override - public @NotNull Pair<@NotNull Boolean, @NotNull Boolean> match(@NotNull IControl control, boolean defaultMatch) { + public @NonNull Pair match(@NonNull IControl control, boolean defaultMatch) { return IControlSelectionFilter.MATCH; } @Override - public @NotNull IControlSelectionFilter getInclusionFilter() { + public @NonNull IControlSelectionFilter getInclusionFilter() { return IControlSelectionFilter.ALL_MATCH; } @Override - public @NotNull IControlSelectionFilter getExclusionFilter() { + public @NonNull IControlSelectionFilter getExclusionFilter() { return IControlSelectionFilter.NONE_MATCH; } }; - @NotNull + @NonNull IControlFilter NONE_MATCH = new IControlFilter() { @Override - public @NotNull Pair<@NotNull Boolean, @NotNull Boolean> match(@NotNull IControl control, boolean defaultMatch) { + public @NonNull Pair match(@NonNull IControl control, boolean defaultMatch) { return IControlSelectionFilter.NON_MATCH; } @Override - public @NotNull IControlSelectionFilter getInclusionFilter() { + public @NonNull IControlSelectionFilter getInclusionFilter() { return IControlSelectionFilter.NONE_MATCH; } @Override - public @NotNull IControlSelectionFilter getExclusionFilter() { + public @NonNull IControlSelectionFilter getExclusionFilter() { return IControlSelectionFilter.NONE_MATCH; } }; @@ -82,14 +83,14 @@ public interface IControlFilter { * an OSCAL profile import statement * @return a new control filter */ - @NotNull - static IControlFilter newInstance(@NotNull ProfileImport profileImport) { + @NonNull + static IControlFilter newInstance(@NonNull ProfileImport profileImport) { return new Filter(profileImport); } - @NotNull - static IControlFilter newInstance(@NotNull IControlSelectionFilter includes, - @NotNull IControlSelectionFilter excludes) { + @NonNull + static IControlFilter newInstance(@NonNull IControlSelectionFilter includes, + @NonNull IControlSelectionFilter excludes) { return new Filter(includes, excludes); } @@ -103,8 +104,8 @@ static IControlFilter newInstance(@NotNull IControlSelectionFilter includes, * @return a pair indicating the status of the match ({@code true} for a match or {@code false} * otherwise), and if a match applies to child controls */ - @NotNull - default Pair<@NotNull Boolean, @NotNull Boolean> match(@NotNull IControl control) { + @NonNull + default Pair match(@NonNull IControl control) { return match(control, false); } @@ -120,22 +121,22 @@ static IControlFilter newInstance(@NotNull IControlSelectionFilter includes, * @return a pair indicating the status of the match ({@code true} for a match or {@code false} * otherwise), and if a match applies to child controls */ - @NotNull - Pair<@NotNull Boolean, @NotNull Boolean> match(@NotNull IControl control, boolean defaultMatch); + @NonNull + Pair match(@NonNull IControl control, boolean defaultMatch); - @NotNull + @NonNull IControlSelectionFilter getInclusionFilter(); - @NotNull + @NonNull IControlSelectionFilter getExclusionFilter(); class Filter implements IControlFilter { - @NotNull + @NonNull private final IControlSelectionFilter inclusionFilter; - @NotNull + @NonNull private final IControlSelectionFilter exclusionFilter; - public Filter(@NotNull ProfileImport profileImport) { + public Filter(@NonNull ProfileImport profileImport) { IncludeAll includeAll = profileImport.getIncludeAll(); if (includeAll == null) { @@ -158,31 +159,31 @@ public Filter(@NotNull ProfileImport profileImport) { } - public Filter(@NotNull IControlSelectionFilter includes, @NotNull IControlSelectionFilter excludes) { + public Filter(@NonNull IControlSelectionFilter includes, @NonNull IControlSelectionFilter excludes) { this.inclusionFilter = includes; this.exclusionFilter = excludes; } @Override - @NotNull + @NonNull public IControlSelectionFilter getInclusionFilter() { return inclusionFilter; } @Override - @NotNull + @NonNull public IControlSelectionFilter getExclusionFilter() { return exclusionFilter; } @Override - public Pair<@NotNull Boolean, @NotNull Boolean> match(@NotNull IControl control, boolean defaultMatch) { - @NotNull - Pair<@NotNull Boolean, @NotNull Boolean> result = getInclusionFilter().apply(control); + public Pair match(@NonNull IControl control, boolean defaultMatch) { + @NonNull + Pair result = getInclusionFilter().apply(control); boolean left = ObjectUtils.notNull(result.getLeft()); if (left) { // this is a positive include match. Is it excluded? - Pair<@NotNull Boolean, @NotNull Boolean> excluded = getExclusionFilter().apply(control); + Pair excluded = getExclusionFilter().apply(control); if (ObjectUtils.notNull(excluded.getLeft())) { // the effective result is a non-match result = IControlSelectionFilter.NON_MATCH; diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlSelectionFilter.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlSelectionFilter.java index f6a1afd0..e5bd9b20 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlSelectionFilter.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IControlSelectionFilter.java @@ -30,44 +30,44 @@ import gov.nist.secauto.oscal.lib.model.control.catalog.IControl; import org.apache.commons.lang3.tuple.Pair; -import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; -public interface IControlSelectionFilter extends Function<@NotNull IControl, Pair<@NotNull Boolean, @NotNull Boolean>> { +import edu.umd.cs.findbugs.annotations.NonNull; - @NotNull - Pair<@NotNull Boolean, @NotNull Boolean> NON_MATCH = ObjectUtils.notNull(Pair.of(false, false)); - @NotNull - Pair<@NotNull Boolean, @NotNull Boolean> MATCH = ObjectUtils.notNull(Pair.of(true, true)); +public interface IControlSelectionFilter extends Function> { - @NotNull + @NonNull + Pair NON_MATCH = ObjectUtils.notNull(Pair.of(false, false)); + @NonNull + Pair MATCH = ObjectUtils.notNull(Pair.of(true, true)); + + @NonNull IControlSelectionFilter ALL_MATCH = new IControlSelectionFilter() { @Override - public Pair<@NotNull Boolean, @NotNull Boolean> apply(@NotNull IControl control) { + public Pair apply(IControl control) { return MATCH; } }; - @NotNull + @NonNull IControlSelectionFilter NONE_MATCH = new IControlSelectionFilter() { @Override - public Pair<@NotNull Boolean, @NotNull Boolean> apply(@NotNull IControl control) { + public Pair apply(IControl control) { return NON_MATCH; } }; - @NotNull - static IControlSelectionFilter matchIds(@NotNull String... identifiers) { + @NonNull + static IControlSelectionFilter matchIds(@NonNull String... identifiers) { return new IControlSelectionFilter() { - private Set<@NotNull String> keys = Arrays.stream(identifiers).collect(Collectors.toUnmodifiableSet()); + private Set keys = Arrays.stream(identifiers).collect(Collectors.toUnmodifiableSet()); - @SuppressWarnings("null") @Override - public @NotNull Pair<@NotNull Boolean, @NotNull Boolean> apply(@NotNull IControl control) { + public @NonNull Pair apply(IControl control) { return Pair.of(keys.contains(control.getId()), false); } @@ -84,7 +84,7 @@ static IControlSelectionFilter matchIds(@NotNull String... identifiers) { * @return a pair indicating the status of the match ({@code true} for a match or {@code false} * otherwise), and if a match applies to child controls */ - @NotNull + @NonNull @Override - Pair<@NotNull Boolean, @NotNull Boolean> apply(@NotNull IControl control); + Pair apply(IControl control); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIdentifierMapper.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIdentifierMapper.java index e5979330..81d1c9fe 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIdentifierMapper.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIdentifierMapper.java @@ -28,55 +28,55 @@ import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; public interface IIdentifierMapper { - @NotNull + @NonNull IIdentifierMapper IDENTITY = new IIdentifierMapper() { @Override - public String mapRoleIdentifier(@NotNull String identifier) { + public String mapRoleIdentifier(@NonNull String identifier) { return identifier; } @Override - public String mapControlIdentifier(@NotNull String identifier) { + public String mapControlIdentifier(@NonNull String identifier) { return identifier; } @Override - public String mapGroupIdentifier(@NotNull String identifier) { + public String mapGroupIdentifier(@NonNull String identifier) { return identifier; } @Override - public String mapParameterIdentifier(@NotNull String identifier) { + public String mapParameterIdentifier(@NonNull String identifier) { return identifier; } @Override - public @NotNull String mapPartIdentifier(@NotNull String identifier) { + public @NonNull String mapPartIdentifier(@NonNull String identifier) { return identifier; } }; - @NotNull - String mapRoleIdentifier(@NotNull String identifier); + @NonNull + String mapRoleIdentifier(@NonNull String identifier); - @NotNull - String mapControlIdentifier(@NotNull String identifier); + @NonNull + String mapControlIdentifier(@NonNull String identifier); - @NotNull - String mapGroupIdentifier(@NotNull String identifier); + @NonNull + String mapGroupIdentifier(@NonNull String identifier); - @NotNull - String mapParameterIdentifier(@NotNull String identifier); + @NonNull + String mapParameterIdentifier(@NonNull String identifier); - @NotNull - String mapPartIdentifier(@NotNull String identifier); + @NonNull + String mapPartIdentifier(@NonNull String identifier); - @NotNull - default String mapByItemType(@NotNull ItemType itemType, @NotNull String identifier) { // NOPMD - intentional + @NonNull + default String mapByItemType(@NonNull ItemType itemType, @NonNull String identifier) { // NOPMD - intentional String retval; switch (itemType) { case CONTROL: diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIndexer.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIndexer.java index c09772c1..93e2d530 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIndexer.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IIndexer.java @@ -28,25 +28,25 @@ import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; public interface IIndexer { - @NotNull + @NonNull Index getIndex(); - EntityItem addRole(@NotNull IRequiredValueModelNodeItem role); + EntityItem addRole(@NonNull IRequiredValueModelNodeItem role); - EntityItem addLocation(@NotNull IRequiredValueModelNodeItem location); + EntityItem addLocation(@NonNull IRequiredValueModelNodeItem location); - EntityItem addParty(@NotNull IRequiredValueModelNodeItem party); + EntityItem addParty(@NonNull IRequiredValueModelNodeItem party); - EntityItem addGroup(@NotNull IRequiredValueModelNodeItem group, boolean selected); + EntityItem addGroup(@NonNull IRequiredValueModelNodeItem group, boolean selected); - EntityItem addControl(@NotNull IRequiredValueModelNodeItem control, boolean selected); + EntityItem addControl(@NonNull IRequiredValueModelNodeItem control, boolean selected); - EntityItem addParameter(@NotNull IRequiredValueModelNodeItem parameter); + EntityItem addParameter(@NonNull IRequiredValueModelNodeItem parameter); - EntityItem addPart(@NotNull IRequiredValueModelNodeItem part); + EntityItem addPart(@NonNull IRequiredValueModelNodeItem part); - EntityItem addResource(@NotNull IRequiredValueModelNodeItem resource); + EntityItem addResource(@NonNull IRequiredValueModelNodeItem resource); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IResult.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IResult.java index 2ff091bb..1783b186 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IResult.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/IResult.java @@ -31,36 +31,36 @@ import gov.nist.secauto.oscal.lib.model.Control; import gov.nist.secauto.oscal.lib.model.Parameter; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.Collection; import java.util.Set; public interface IResult { - @NotNull - Collection<@NotNull Parameter> getPromotedParameters(); + @NonNull + Collection getPromotedParameters(); - @NotNull - Collection<@NotNull Control> getPromotedControls(); + @NonNull + Collection getPromotedControls(); - @NotNull - Set<@NotNull String> getRequiredParameterIds(); + @NonNull + Set getRequiredParameterIds(); - boolean isParameterRequired(@NotNull String id); + boolean isParameterRequired(@NonNull String id); - void promoteParameter(@NotNull Parameter param); + void promoteParameter(@NonNull Parameter param); - void promoteControl(@NotNull Control control); + void promoteControl(@NonNull Control control); - void requireParameters(@NotNull Set<@NotNull String> requiredParameterIds); + void requireParameters(@NonNull Set requiredParameterIds); - void applyTo(@NotNull Catalog parent); + void applyTo(@NonNull Catalog parent); - void applyTo(@NotNull CatalogGroup parent); + void applyTo(@NonNull CatalogGroup parent); - void applyTo(@NotNull Control parent); + void applyTo(@NonNull Control parent); - @NotNull - IResult append(@NotNull IResult that); + @NonNull + IResult append(@NonNull IResult that); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Import.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Import.java index d9960496..e4aad828 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Import.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Import.java @@ -29,7 +29,6 @@ import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.core.util.VersionUtil; -import gov.nist.secauto.metaschema.binding.io.BindingException; import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; import gov.nist.secauto.metaschema.model.common.metapath.item.IModelNodeItem; import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; @@ -45,23 +44,23 @@ import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; import gov.nist.secauto.oscal.lib.profile.resolver.policy.ReferenceCountingVisitor; -import org.jetbrains.annotations.NotNull; - import java.net.URI; import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; +import edu.umd.cs.findbugs.annotations.NonNull; + public class Import { - @NotNull + @NonNull private final IDocumentNodeItem profileDocument; - @NotNull + @NonNull private final IModelNodeItem profileImportItem; public Import( - @NotNull IDocumentNodeItem profileDocument, - @NotNull IModelNodeItem profileImportItem) throws BindingException { + @NonNull IDocumentNodeItem profileDocument, + @NonNull IModelNodeItem profileImportItem) { this.profileDocument = profileDocument; this.profileImportItem = profileImportItem; @@ -76,26 +75,27 @@ protected IModelNodeItem getProfileImportItem() { } @SuppressWarnings("null") - @NotNull + @NonNull protected ProfileImport getProfileImport() { - return (@NotNull ProfileImport) profileImportItem.getValue(); + return ObjectUtils.requireNonNull((ProfileImport) profileImportItem.getValue()); } private Catalog toCatalog(IDocumentNodeItem catalogDocument) { return (Catalog) catalogDocument.getValue(); } - @NotNull + @NonNull protected IControlFilter newControlFilter() { return IControlFilter.newInstance(getProfileImport()); } - @NotNull + @NonNull protected IIdentifierMapper newIdentifierMapper() { return IIdentifierMapper.IDENTITY; } - public Index resolve(@NotNull IDocumentNodeItem importedCatalogDocument, @NotNull Catalog resolvedCatalog) { + public Index resolve(@NonNull IDocumentNodeItem importedCatalogDocument, @NonNull Catalog resolvedCatalog) + throws ProfileResolutionException { ProfileImport profileImport = getProfileImport(); URI uri = ObjectUtils.requireNonNull(profileImport.getHref(), "profile import href is null"); @@ -103,15 +103,22 @@ public Index resolve(@NotNull IDocumentNodeItem importedCatalogDocument, @NotNul IControlFilter filter = newControlFilter(); IIdentifierMapper mapper = newIdentifierMapper(); ControlSelectionVisitor selectionVisitor = new ControlSelectionVisitor(filter, mapper); - selectionVisitor.visitCatalog(importedCatalogDocument); - Index index = selectionVisitor.getIndex(); - // process references - new ReferenceCountingVisitor(index, uri).visitCatalog(importedCatalogDocument); + Index index; + try { + selectionVisitor.visitCatalog(importedCatalogDocument); + index = selectionVisitor.getIndex(); - // filter based on selections - FilterNonSelectedVisitor pruneVisitor = new FilterNonSelectedVisitor(index); - pruneVisitor.visitCatalog(importedCatalogDocument); + // process references + new ReferenceCountingVisitor(index, uri).visitCatalog(importedCatalogDocument); + + // filter based on selections + FilterNonSelectedVisitor pruneVisitor = new FilterNonSelectedVisitor(index); + pruneVisitor.visitCatalog(importedCatalogDocument); + } catch (ProfileResolutionEvaluationException ex) { + throw new ProfileResolutionException( + String.format("Unable to resolve profile import '%s'. %s", uri.toString(), ex.getMessage()), ex); + } Catalog importedCatalog = toCatalog(importedCatalogDocument); for (Parameter param : CollectionUtil.listOrEmpty(importedCatalog.getParams())) { @@ -135,8 +142,8 @@ public Index resolve(@NotNull IDocumentNodeItem importedCatalogDocument, @NotNul return index; } - private void generateMetadata(@NotNull IDocumentNodeItem importedCatalogDocument, @NotNull Catalog resolvedCatalog, - @NotNull Index index) { + private void generateMetadata(@NonNull IDocumentNodeItem importedCatalogDocument, @NonNull Catalog resolvedCatalog, + @NonNull Index index) { Metadata importedMetadata = toCatalog(importedCatalogDocument).getMetadata(); if (importedMetadata != null) { @@ -183,7 +190,7 @@ private void generateMetadata(@NotNull IDocumentNodeItem importedCatalogDocument } } - private void generateBackMatter(@NotNull IDocumentNodeItem importedCatalogDocument, @NotNull Catalog resolvedCatalog, + private void generateBackMatter(@NonNull IDocumentNodeItem importedCatalogDocument, @NonNull Catalog resolvedCatalog, Index index) { BackMatter importedBackMatter = toCatalog(importedCatalogDocument).getBackMatter(); diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportCycleException.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportCycleException.java index b6cf0f3b..f5e50cc1 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportCycleException.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportCycleException.java @@ -53,5 +53,4 @@ public ImportCycleException(String message, Throwable cause) { public ImportCycleException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } - } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Index.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Index.java index 7cc1400f..83c23131 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Index.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/Index.java @@ -26,17 +26,16 @@ package gov.nist.secauto.oscal.lib.profile.resolver; +import gov.nist.secauto.metaschema.model.common.datatype.adapter.UuidAdapter; import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression; import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression.ResultType; import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; import gov.nist.secauto.metaschema.model.common.util.CustomCollectors; +import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; import gov.nist.secauto.oscal.lib.model.control.catalog.ICatalog; import gov.nist.secauto.oscal.lib.model.control.catalog.IControlContainer; import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - import java.util.Collection; import java.util.EnumMap; import java.util.HashSet; @@ -47,20 +46,23 @@ import java.util.function.Function; import java.util.stream.Stream; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + public class Index { public static final MetapathExpression HAS_PROP_KEEP = MetapathExpression .compile("prop[@name='keep' and has-oscal-namespace('http://csrc.nist.gov/ns/oscal')]/@value = 'always'"); - @NotNull + @NonNull private final Map entityMap; - @NotNull + @NonNull private final Set selectedContainers; public static Stream merge( - @NotNull Stream resolvedItems, - @NotNull Index index, - @NotNull ItemType itemType, - @NotNull Function keyMapper) { + @NonNull Stream resolvedItems, + @NonNull Index index, + @NonNull ItemType itemType, + @NonNull Function keyMapper) { @SuppressWarnings("unchecked") Stream importedStream = index.getEntitiesByItemType(itemType).stream() .filter(entity -> { @@ -81,43 +83,44 @@ public Index() { this.selectedContainers = new HashSet<>(); } - public void markSelected(@NotNull IControlContainer container) { + public void markSelected(@NonNull IControlContainer container) { selectedContainers.add(container); } - public boolean isSelected(@NotNull IControlContainer container) { + public boolean isSelected(@NonNull IControlContainer container) { return container instanceof ICatalog || selectedContainers.contains(container); } @Nullable - protected ItemGroup getItemGroup(@NotNull ItemType itemType) { + protected ItemGroup getItemGroup(@NonNull ItemType itemType) { return entityMap.get(itemType); } - protected ItemGroup newItemGroup(@NotNull ItemType itemType) { + protected ItemGroup newItemGroup(@NonNull ItemType itemType) { ItemGroup retval = new ItemGroup(itemType); entityMap.put(itemType, retval); return retval; } - @NotNull - public Collection<@NotNull EntityItem> - getEntitiesByItemType(@NotNull ItemType itemType) { + @NonNull + public Collection + getEntitiesByItemType(@NonNull ItemType itemType) { ItemGroup group = getItemGroup(itemType); return group == null ? CollectionUtil.emptyList() : group.getEntities(); } - @SuppressWarnings("null") - public EntityItem getEntity(@NotNull ItemType itemType, @NotNull UUID identifier) { + public EntityItem getEntity(@NonNull ItemType itemType, @NonNull UUID identifier) { return getEntity(itemType, identifier.toString()); } - public EntityItem getEntity(@NotNull ItemType itemType, @NotNull String identifier) { + public EntityItem getEntity(@NonNull ItemType itemType, @NonNull String identifier) { ItemGroup group = getItemGroup(itemType); - return group == null ? null : group.getEntity(identifier); + + String normalizedIdentifier = EntityItem.normalizeIdentifier(itemType, identifier); + return group == null ? null : group.getEntity(normalizedIdentifier); } - public EntityItem addItem(@NotNull EntityItem item) { + public EntityItem addItem(@NonNull EntityItem item) { ItemType type = item.getItemType(); ItemGroup group = getItemGroup(type); @@ -136,13 +139,13 @@ public EntityItem addItem(@NotNull EntityItem item) { // return count; // } // - // public void incrementParameterReferenceCount(@NotNull String parameterId) { + // public void incrementParameterReferenceCount(@NonNull String parameterId) { // int count = getParameterReferenceCount(parameterId); // // parameterReferenceCountMap.put(parameterId, ++count); // } // - // public void decrementParameterReferenceCount(@NotNull String parameterId) { + // public void decrementParameterReferenceCount(@NonNull String parameterId) { // int count = getParameterReferenceCount(parameterId); // // if (count == 0) { @@ -159,26 +162,25 @@ public EntityItem addItem(@NotNull EntityItem item) { // } private static class ItemGroup { - @NotNull + @NonNull private final ItemType itemType; - Map<@NotNull String, EntityItem> idToEntityMap; + Map idToEntityMap; - public ItemGroup(@NotNull ItemType itemType) { + public ItemGroup(@NonNull ItemType itemType) { this.itemType = itemType; this.idToEntityMap = new LinkedHashMap<>(); } - public EntityItem getEntity(@NotNull String identifier) { + public EntityItem getEntity(@NonNull String identifier) { return idToEntityMap.get(identifier); } - @SuppressWarnings("null") - @NotNull - public Collection<@NotNull EntityItem> getEntities() { + @NonNull + public Collection getEntities() { return idToEntityMap.values(); } - public EntityItem add(@NotNull EntityItem entity) { + public EntityItem add(@NonNull EntityItem entity) { assert itemType.equals(entity.getItemType()); return idToEntityMap.put(entity.getOriginalIdentifier(), entity); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ModifyPhaseUtils.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ModifyPhaseUtils.java new file mode 100644 index 00000000..bd0c8804 --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ModifyPhaseUtils.java @@ -0,0 +1,109 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver; + +import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public final class ModifyPhaseUtils { + private ModifyPhaseUtils() { + // disable construction + } + + + public static Function identityKey() { + return (item) -> Integer.toString(Objects.hashCode(item)); + } + + public static Function identifierKey(@NonNull Function identifierFunction) { + return (item) -> { + R identifier = identifierFunction.apply(item); + String retval; + if (identifier == null) { + retval = Integer.toString(Objects.hashCode(item)); + } else { + retval = identifier.toString(); + } + return retval; + }; + } + + public static T mergeItem(@Nullable T original, @Nullable T additional) { + if (additional == null) { + return original; // NOPMD - readability + } + + return additional; + } + + public static List merge(@Nullable List original, @Nullable List additional, + Function keyFunction) { + if (additional == null || additional.isEmpty()) { + return original; // NOPMD - readability + } + + if (original == null || original.isEmpty()) { + return additional; // NOPMD - readability + } + + // reverse the stream + List reversed = Stream.concat( + CollectionUtil.listOrEmpty(original).stream(), + CollectionUtil.listOrEmpty(additional).stream()) + .collect(Collectors.collectingAndThen( + Collectors.toList(), + l -> { + Collections.reverse(l); + return l; + })); + + // build a map of each unique identity + Map> identityMap = reversed.stream() + .collect(Collectors.groupingBy(keyFunction, LinkedHashMap::new, Collectors.toList())); + + // build a reversed list of items, using the first item + return identityMap.values().stream() + .map(list -> list.stream().findFirst().get()) + .collect(Collectors.collectingAndThen( + Collectors.toList(), + l -> { + Collections.reverse(l); + return l; + })); + } +} diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionEvaluationException.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionEvaluationException.java new file mode 100644 index 00000000..1a13573a --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionEvaluationException.java @@ -0,0 +1,51 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver; + +public class ProfileResolutionEvaluationException + extends IllegalStateException { + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 1L; + + public ProfileResolutionEvaluationException() { + } + + public ProfileResolutionEvaluationException(String s) { + super(s); + } + + public ProfileResolutionEvaluationException(Throwable cause) { + super(cause); + } + + public ProfileResolutionEvaluationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionException.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionException.java new file mode 100644 index 00000000..a3d0e3ba --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionException.java @@ -0,0 +1,61 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver; + +import edu.umd.cs.findbugs.annotations.NonNull; + +public class ProfileResolutionException + extends Exception { + + /** + * the serial version UID. + */ + private static final long serialVersionUID = 1L; + + /** + * Create a new profile resolution exception with the provided {@code message}. + * + * @param message + * a description of the error that occurred + */ + public ProfileResolutionException(String message) { + super(message); + } + + /** + * Create a new profile resolution exception with the provided {@code message} based on the provided + * {@code cause}. + * + * @param message + * a description of the error that occurred + * @param cause + * the initial cause of the exception + */ + public ProfileResolutionException(String message, @NonNull Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolver.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolver.java index 606982c9..ada7d0af 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolver.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolver.java @@ -32,10 +32,14 @@ import gov.nist.secauto.metaschema.binding.model.IAssemblyClassBinding; import gov.nist.secauto.metaschema.binding.model.RootAssemblyDefinition; import gov.nist.secauto.metaschema.model.common.metapath.DynamicContext; +import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression; import gov.nist.secauto.metaschema.model.common.metapath.StaticContext; +import gov.nist.secauto.metaschema.model.common.metapath.format.IPathFormatter; import gov.nist.secauto.metaschema.model.common.metapath.item.DefaultNodeItemFactory; import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; +import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueAssemblyNodeItem; import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; +import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueNodeItem; import gov.nist.secauto.metaschema.model.common.metapath.item.IRootAssemblyNodeItem; import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; @@ -44,18 +48,23 @@ import gov.nist.secauto.oscal.lib.model.BackMatter; import gov.nist.secauto.oscal.lib.model.BackMatter.Resource; import gov.nist.secauto.oscal.lib.model.Catalog; +import gov.nist.secauto.oscal.lib.model.Control; import gov.nist.secauto.oscal.lib.model.Link; import gov.nist.secauto.oscal.lib.model.Merge; import gov.nist.secauto.oscal.lib.model.Metadata; +import gov.nist.secauto.oscal.lib.model.Modify; +import gov.nist.secauto.oscal.lib.model.Modify.ProfileSetParameter; +import gov.nist.secauto.oscal.lib.model.Parameter; import gov.nist.secauto.oscal.lib.model.Profile; import gov.nist.secauto.oscal.lib.model.ProfileImport; import gov.nist.secauto.oscal.lib.model.Property; import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; +import gov.nist.secauto.oscal.lib.profile.resolver.alter.AddVisitor; +import gov.nist.secauto.oscal.lib.profile.resolver.alter.RemoveVisitor; import gov.nist.secauto.oscal.lib.profile.resolver.policy.ReferenceCountingVisitor; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -75,8 +84,19 @@ import java.util.UUID; import java.util.stream.Collectors; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + public class ProfileResolver { // NOPMD - ok private static final Logger LOGGER = LogManager.getLogger(ProfileResolver.class); + private static final MetapathExpression METAPATH_SET_PARAMETER + = MetapathExpression.compile("/profile/modify/set-parameter"); + private static final MetapathExpression METAPATH_ALTER + = MetapathExpression.compile("/profile/modify/alter"); + private static final MetapathExpression METAPATH_ALTER_REMOVE + = MetapathExpression.compile("remove"); + private static final MetapathExpression METAPATH_ALTER_ADD + = MetapathExpression.compile("add"); public enum StructuringDirective { FLAT, @@ -92,85 +112,66 @@ public enum StructuringDirective { * * @return the bound loader */ - @NotNull + @NonNull public IBoundLoader getBoundLoader() { synchronized (this) { if (loader == null) { loader = OscalBindingContext.instance().newBoundLoader(); loader.disableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS); } + assert loader != null; + return loader; } - assert loader != null; - return loader; } - public void setBoundLoader(@NotNull IBoundLoader loader) { + public void setBoundLoader(@NonNull IBoundLoader loader) { synchronized (this) { this.loader = loader; } } - @NotNull + @NonNull + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to expose this field") public DynamicContext getDynamicContext() { synchronized (this) { if (dynamicContext == null) { dynamicContext = new StaticContext().newDynamicContext(); dynamicContext.setDocumentLoader(getBoundLoader()); } + assert dynamicContext != null; + return dynamicContext; } - assert dynamicContext != null; - return dynamicContext; } - public void setDynamicContext(@NotNull DynamicContext dynamicContext) { + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to store this parameter") + public void setDynamicContext(@NonNull DynamicContext dynamicContext) { synchronized (this) { this.dynamicContext = dynamicContext; } } - @NotNull - protected EntityResolver getEntityResolver(@NotNull URI documentUri) { + @NonNull + protected EntityResolver getEntityResolver(@NonNull URI documentUri) { return new DocumentEntityResolver(documentUri); } - public IDocumentNodeItem resolveProfile(@NotNull URL url) throws URISyntaxException, IOException { + public IDocumentNodeItem resolveProfile(@NonNull URL url) + throws URISyntaxException, IOException, ProfileResolutionException { IBoundLoader loader = getBoundLoader(); IDocumentNodeItem catalogOrProfile = loader.loadAsNodeItem(url); return resolve(catalogOrProfile); } - public IDocumentNodeItem resolveProfile(@NotNull Path path) throws IOException { + public IDocumentNodeItem resolveProfile(@NonNull Path path) throws IOException, ProfileResolutionException { IBoundLoader loader = getBoundLoader(); IDocumentNodeItem catalogOrProfile = loader.loadAsNodeItem(path); return resolve(catalogOrProfile); } - public IDocumentNodeItem resolveProfile(@NotNull File file) throws IOException { + public IDocumentNodeItem resolveProfile(@NonNull File file) throws IOException, ProfileResolutionException { return resolveProfile(ObjectUtils.notNull(file.toPath())); } - @NotNull - public IDocumentNodeItem resolve(@NotNull IDocumentNodeItem profileOrCatalog) throws IOException { - return resolve(profileOrCatalog, new Stack<>()); - } - - @NotNull - protected IDocumentNodeItem resolve(@NotNull IDocumentNodeItem profileOrCatalog, - @NotNull Stack<@NotNull URI> importHistory) - throws IOException { - Object profileObject = profileOrCatalog.getValue(); - - IDocumentNodeItem retval; - if (profileObject instanceof Catalog) { - // already a catalog - retval = profileOrCatalog; - } else { - // must be a profile - retval = resolveProfile(profileOrCatalog, importHistory); - } - return retval; - } - /** * Resolve the profile to a catalog. * @@ -180,43 +181,70 @@ protected IDocumentNodeItem resolve(@NotNull IDocumentNodeItem profileOrCatalog, * the import stack for cycle detection * @return the resolved profile * @throws IOException - * if an error occurs while loading the profile or an import + * if an error occurred while loading the profile or an import + * @throws ProfileResolutionException + * if an error occurred while resolving the profile */ - @NotNull + @NonNull protected IDocumentNodeItem resolveProfile( - @NotNull IDocumentNodeItem profileDocument, - @NotNull Stack<@NotNull URI> importHistory) throws IOException { + @NonNull IDocumentNodeItem profileDocument, + @NonNull Stack importHistory) throws IOException, ProfileResolutionException { Catalog resolvedCatalog = new Catalog(); generateMetadata(resolvedCatalog, profileDocument); resolveImports(resolvedCatalog, profileDocument, importHistory); handleMerge(resolvedCatalog, profileDocument); + handleModify(resolvedCatalog, profileDocument); handleReferences(resolvedCatalog, profileDocument); return DefaultNodeItemFactory.instance().newDocumentNodeItem( new RootAssemblyDefinition( - (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class)), + ObjectUtils.notNull( + (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class))), resolvedCatalog, profileDocument.getBaseUri()); } - private Profile toProfile(@NotNull IDocumentNodeItem profileDocument) { + @NonNull + public IDocumentNodeItem resolve(@NonNull IDocumentNodeItem profileOrCatalog) + throws IOException, ProfileResolutionException { + return resolve(profileOrCatalog, new Stack<>()); + } + + @NonNull + protected IDocumentNodeItem resolve(@NonNull IDocumentNodeItem profileOrCatalog, + @NonNull Stack importHistory) + throws IOException, ProfileResolutionException { + Object profileObject = profileOrCatalog.getValue(); + + IDocumentNodeItem retval; + if (profileObject instanceof Catalog) { + // already a catalog + retval = profileOrCatalog; + } else { + // must be a profile + retval = resolveProfile(profileOrCatalog, importHistory); + } + return retval; + } + + private static Profile toProfile(@NonNull IDocumentNodeItem profileDocument) { Object object = profileDocument.getValue(); assert object != null; return (Profile) object; } - @NotNull - private static Profile toProfile(@NotNull IRootAssemblyNodeItem profileItem) { + @NonNull + private static Profile toProfile(@NonNull IRootAssemblyNodeItem profileItem) { Object object = profileItem.getValue(); assert object != null; return (Profile) object; } - private void generateMetadata(@NotNull Catalog resolvedCatalog, @NotNull IDocumentNodeItem profileDocument) { + private static void generateMetadata(@NonNull Catalog resolvedCatalog, @NonNull IDocumentNodeItem profileDocument) { resolvedCatalog.setUuid(UUID.randomUUID()); Profile profile = toProfile(profileDocument); @@ -242,16 +270,16 @@ private void generateMetadata(@NotNull Catalog resolvedCatalog, @NotNull IDocume resolvedCatalog.setMetadata(resolvedMetadata); } - private void resolveImports(@NotNull Catalog resolvedCatalog, @NotNull IDocumentNodeItem profileDocument, - @NotNull Stack<@NotNull URI> importHistory) - throws IOException { + private void resolveImports(@NonNull Catalog resolvedCatalog, @NonNull IDocumentNodeItem profileDocument, + @NonNull Stack importHistory) + throws IOException, ProfileResolutionException { IRootAssemblyNodeItem profileItem = profileDocument.getRootAssemblyNodeItem(); // first verify there is at least one import - List<@NotNull ? extends IRequiredValueModelNodeItem> profileImports = profileItem.getModelItemsByName("import"); + List profileImports = profileItem.getModelItemsByName("import"); if (profileImports.isEmpty()) { - throw new IllegalStateException(String.format("Profile '%s' has no imports", profileItem.getBaseUri())); + throw new ProfileResolutionException(String.format("Profile '%s' has no imports", profileItem.getBaseUri())); } // now process each import @@ -261,15 +289,15 @@ private void resolveImports(@NotNull Catalog resolvedCatalog, @NotNull IDocument } protected void resolveImport( - @NotNull IRequiredValueModelNodeItem profileImportItem, - @NotNull IDocumentNodeItem profileDocument, - @NotNull Stack<@NotNull URI> importHistory, - @NotNull Catalog resolvedCatalog) throws IOException { + @NonNull IRequiredValueModelNodeItem profileImportItem, + @NonNull IDocumentNodeItem profileDocument, + @NonNull Stack importHistory, + @NonNull Catalog resolvedCatalog) throws IOException, ProfileResolutionException { ProfileImport profileImport = (ProfileImport) profileImportItem.getValue(); URI importUri = profileImport.getHref(); if (importUri == null) { - throw new IllegalArgumentException("profileImport.getHref() must return a non-null URI"); + throw new ProfileResolutionException("profileImport.getHref() must return a non-null URI"); } if (LOGGER.isDebugEnabled()) { @@ -314,11 +342,11 @@ protected void resolveImport( } } - @NotNull + @NonNull protected InputSource newImportSource( - @NotNull URI importUri, - @NotNull IDocumentNodeItem profileDocument, - @NotNull Stack<@NotNull URI> importHistory) throws IOException { + @NonNull URI importUri, + @NonNull IDocumentNodeItem profileDocument, + @NonNull Stack importHistory) throws IOException { // Get the entity resolver to resolve relative references in the profile EntityResolver resolver = getEntityResolver(profileDocument.getDocumentUri()); @@ -332,7 +360,7 @@ protected InputSource newImportSource( Profile profile = toProfile(profileItem); Resource resource = profile.getResourceByUuid(ObjectUtils.notNull(UUID.fromString(uuid))); if (resource == null) { - throw new IllegalArgumentException( + throw new IOException( String.format("unable to find the resource identified by '%s' used in profile import", importUri)); } @@ -352,8 +380,7 @@ protected InputSource newImportSource( return source; } - @NotNull - private void requireNonCycle(@NotNull URI uri, @NotNull Stack<@NotNull URI> importHistory) + private static void requireNonCycle(@NonNull URI uri, @NonNull Stack importHistory) throws ImportCycleException { List cycle = checkCycle(uri, importHistory); if (!cycle.isEmpty()) { @@ -362,9 +389,8 @@ private void requireNonCycle(@NotNull URI uri, @NotNull Stack<@NotNull URI> impo } } - @SuppressWarnings("null") - @NotNull - private List checkCycle(@NotNull URI uri, @NotNull Stack<@NotNull URI> importHistory) { + @NonNull + private static List checkCycle(@NonNull URI uri, @NonNull Stack importHistory) { int index = importHistory.indexOf(uri); List retval; @@ -376,7 +402,8 @@ private List checkCycle(@NotNull URI uri, @NotNull Stack<@NotNull URI> impo return retval; } - private StructuringDirective getStructuringDirective(Profile profile) { + // TODO: move this to an abstract method on profile + private static StructuringDirective getStructuringDirective(Profile profile) { Merge merge = profile.getMerge(); StructuringDirective retval; @@ -392,7 +419,7 @@ private StructuringDirective getStructuringDirective(Profile profile) { return retval; } - private void handleMerge(@NotNull Catalog resolvedCatalog, @NotNull IDocumentNodeItem profileDocument) { + protected void handleMerge(@NonNull Catalog resolvedCatalog, @NonNull IDocumentNodeItem profileDocument) { // handle combine // handle structuring @@ -410,17 +437,145 @@ private void handleMerge(@NotNull Catalog resolvedCatalog, @NotNull IDocumentNod } - private void structureFlat(@NotNull Catalog catalog) { + protected void handleModify(@NonNull Catalog resolvedCatalog, @NonNull IDocumentNodeItem profileDocument) + throws ProfileResolutionException { + IDocumentNodeItem resolvedCatalogDocument = DefaultNodeItemFactory.instance().newDocumentNodeItem( + new RootAssemblyDefinition( + ObjectUtils.notNull( + (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class))), + resolvedCatalog, + profileDocument.getBaseUri()); + + try { + ControlIndexingVisitor visitor = new ControlIndexingVisitor(IIdentifierMapper.IDENTITY); + visitor.visitCatalog(resolvedCatalogDocument, null); + Index index = visitor.getIndex(); + + METAPATH_SET_PARAMETER.evaluate(profileDocument) + .forEach(item -> { + IRequiredValueAssemblyNodeItem setParameter = (IRequiredValueAssemblyNodeItem) item; + try { + handleSetParameter(setParameter, index); + } catch (ProfileResolutionEvaluationException ex) { + throw new ProfileResolutionEvaluationException( + String.format("Unable to apply the set-parameter at '%s' in '%s'. %s", + setParameter.toPath(IPathFormatter.METAPATH_PATH_FORMATER), + ObjectUtils.notNull(setParameter.getBaseUri()).toString(), + ex.getLocalizedMessage()), + ex); + } + }); + + METAPATH_ALTER.evaluate(profileDocument) + .forEach(item -> { + handleAlter((IRequiredValueAssemblyNodeItem) item, index); + }); + } catch (ProfileResolutionEvaluationException ex) { + throw new ProfileResolutionException( + String.format("Unable to apply the modify in '%s'. %s", + resolvedCatalogDocument.getDocumentUri().toString(), + ex.getLocalizedMessage()), + ex); + } + } + + protected void handleSetParameter(IRequiredValueAssemblyNodeItem item, Index index) { + ProfileSetParameter setParameter = (Modify.ProfileSetParameter) item.getValue(); + String paramId = setParameter.getParamId(); + EntityItem entity = index.getEntity(ItemType.PARAMETER, paramId); + if (entity == null) { + throw new IllegalStateException( + String.format("The parameter '%s' was not found in the resolved catalog", paramId)); + } + + Parameter param = entity.getInstanceValue(); + + // apply the set parameter values + param.setClazz(ModifyPhaseUtils.mergeItem(param.getClazz(), setParameter.getClazz())); + param.setProps(ModifyPhaseUtils.merge(param.getProps(), setParameter.getProps(), + ModifyPhaseUtils.identifierKey(Property::getUuid))); + param.setLinks(ModifyPhaseUtils.merge(param.getLinks(), setParameter.getLinks(), ModifyPhaseUtils.identityKey())); + param.setLabel(ModifyPhaseUtils.mergeItem(param.getLabel(), setParameter.getLabel())); + param.setUsage(ModifyPhaseUtils.mergeItem(param.getUsage(), setParameter.getUsage())); + param.setConstraints( + ModifyPhaseUtils.merge(param.getConstraints(), setParameter.getConstraints(), ModifyPhaseUtils.identityKey())); + param.setGuidelines( + ModifyPhaseUtils.merge(param.getGuidelines(), setParameter.getGuidelines(), ModifyPhaseUtils.identityKey())); + param.setValues(new LinkedList<>(setParameter.getValues())); + param.setSelect(setParameter.getSelect()); + } + + protected void handleAlter(IRequiredValueAssemblyNodeItem item, Index index) { + Modify.Alter alter = (Modify.Alter) item.getValue(); + String controlId = alter.getControlId(); + EntityItem entity = index.getEntity(ItemType.CONTROL, controlId); + Control control = entity.getInstanceValue(); + + METAPATH_ALTER_REMOVE.evaluate(item) + .forEach(nodeItem -> { + IRequiredValueNodeItem removeItem = (IRequiredValueNodeItem) nodeItem; + Modify.Alter.Remove remove = ObjectUtils.notNull((Modify.Alter.Remove) removeItem.getValue()); + + try { + if (!RemoveVisitor.remove( + control, + remove.getByName(), + remove.getByClass(), + remove.getById(), + remove.getByNs(), + RemoveVisitor.TargetType.forFieldName(remove.getByItemName()))) { + throw new ProfileResolutionEvaluationException( + String.format("The remove did not match a valid target")); + } + } catch (ProfileResolutionEvaluationException ex) { + throw new ProfileResolutionEvaluationException( + String.format("Unable to apply the remove at '%s' in '%s'. %s", + removeItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER), + ObjectUtils.notNull(removeItem.getBaseUri()).toString(), + ex.getLocalizedMessage()), + ex); + } + }); + METAPATH_ALTER_ADD.evaluate(item) + .forEach(nodeItem -> { + IRequiredValueNodeItem addItem = (IRequiredValueNodeItem) nodeItem; + Modify.Alter.Add add = ObjectUtils.notNull((Modify.Alter.Add) addItem.getValue()); + try { + if (!AddVisitor.add( + control, + AddVisitor.Position.forName(add.getPosition()), + add.getById(), + add.getTitle(), + CollectionUtil.listOrEmpty(add.getParams()), + CollectionUtil.listOrEmpty(add.getProps()), + CollectionUtil.listOrEmpty(add.getLinks()), + CollectionUtil.listOrEmpty(add.getParts()))) { + throw new ProfileResolutionEvaluationException( + String.format("The add did not match a valid target")); + } + } catch (ProfileResolutionEvaluationException ex) { + throw new ProfileResolutionEvaluationException( + String.format("Unable to apply the add at '%s' in '%s'. %s", + addItem.toPath(IPathFormatter.METAPATH_PATH_FORMATER), + ObjectUtils.notNull(addItem.getBaseUri()).toString(), + ex.getLocalizedMessage()), + ex); + } + }); + } + + protected void structureFlat(@NonNull Catalog catalog) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("applying flat structuring directive"); } new FlatStructureCatalogVisitor().visitCatalog(catalog); } - private void handleReferences(@NotNull Catalog resolvedCatalog, @NotNull IDocumentNodeItem profileDocument) { + private static void handleReferences(@NonNull Catalog resolvedCatalog, @NonNull IDocumentNodeItem profileDocument) { IDocumentNodeItem resolvedCatalogItem = DefaultNodeItemFactory.instance().newDocumentNodeItem( new RootAssemblyDefinition( - (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class)), + ObjectUtils.notNull( + (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class))), resolvedCatalog, profileDocument.getBaseUri()); @@ -484,14 +639,14 @@ private void handleReferences(@NotNull Catalog resolvedCatalog, @NotNull IDocume } private class DocumentEntityResolver implements EntityResolver { - @NotNull + @NonNull private final URI documentUri; - public DocumentEntityResolver(@NotNull URI documentUri) { + public DocumentEntityResolver(@NonNull URI documentUri) { this.documentUri = documentUri; } - @NotNull + @NonNull protected URI getDocumentUri() { return documentUri; } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/AddVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/AddVisitor.java new file mode 100644 index 00000000..0925eed3 --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/AddVisitor.java @@ -0,0 +1,701 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver.alter; + +import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupLine; +import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; +import gov.nist.secauto.metaschema.model.common.util.CustomCollectors; +import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; +import gov.nist.secauto.oscal.lib.model.Catalog; +import gov.nist.secauto.oscal.lib.model.CatalogGroup; +import gov.nist.secauto.oscal.lib.model.Control; +import gov.nist.secauto.oscal.lib.model.ControlPart; +import gov.nist.secauto.oscal.lib.model.Link; +import gov.nist.secauto.oscal.lib.model.Parameter; +import gov.nist.secauto.oscal.lib.model.Property; +import gov.nist.secauto.oscal.lib.model.control.catalog.ICatalogVisitor; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException; + +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public class AddVisitor implements ICatalogVisitor { + public enum TargetType { + CONTROL("control", Control.class), + PARAM("param", Parameter.class), + PART("part", ControlPart.class); + + @NonNull + private static final Map, TargetType> CLASS_TO_TYPE; + @NonNull + private static final Map NAME_TO_TYPE; + @NonNull + private final String fieldName; + @NonNull + private final Class clazz; + + static { + { + Map, TargetType> map = new ConcurrentHashMap<>(); + for (TargetType type : TargetType.values()) { + map.put(type.getClazz(), type); + } + CLASS_TO_TYPE = CollectionUtil.unmodifiableMap(map); + } + + { + Map map = new ConcurrentHashMap<>(); + for (TargetType type : TargetType.values()) { + map.put(type.fieldName(), type); + } + NAME_TO_TYPE = CollectionUtil.unmodifiableMap(map); + } + } + + @Nullable + public static TargetType forClass(@NonNull Class clazz) { + Class target = clazz; + TargetType retval; + // recurse over parent classes to find a match + do { + retval = CLASS_TO_TYPE.get(target); + } while (retval == null && (target = target.getSuperclass()) != null); + return retval; + } + + @Nullable + public static TargetType forFieldName(@Nullable String name) { + return name == null ? null : NAME_TO_TYPE.get(name); + } + + TargetType(@NonNull String fieldName, @NonNull Class clazz) { + this.fieldName = fieldName; + this.clazz = clazz; + } + + public String fieldName() { + return fieldName; + } + + public Class getClazz() { + return clazz; + } + } + + public enum Position { + BEFORE, + AFTER, + STARTING, + ENDING; + + @NonNull + private static final Map NAME_TO_POSITION; + + static { + Map map = new ConcurrentHashMap<>(); + for (Position position : Position.values()) { + map.put(position.name().toLowerCase(Locale.ROOT), position); + } + NAME_TO_POSITION = CollectionUtil.unmodifiableMap(map); + } + + @Nullable + public static Position forName(@Nullable String name) { + return name == null ? null : NAME_TO_POSITION.get(name); + } + } + + @NonNull + private static final AddVisitor INSTANCE = new AddVisitor(); + private static final Map> APPLICABLE_TARGETS; + + static { + APPLICABLE_TARGETS = new EnumMap<>(TargetType.class); + APPLICABLE_TARGETS.put(TargetType.CONTROL, Set.of(TargetType.CONTROL, TargetType.PARAM, TargetType.PART)); + APPLICABLE_TARGETS.put(TargetType.PARAM, Set.of(TargetType.PARAM)); + APPLICABLE_TARGETS.put(TargetType.PART, Set.of(TargetType.PART)); + } + + private static Set getApplicableTypes(@NonNull TargetType type) { + return APPLICABLE_TARGETS.getOrDefault(type, CollectionUtil.emptySet()); + } + + /** + * Apply the add directive. + * + * @param control + * the control target + * @param position + * the position to apply the content or {@code null} + * @param byId + * the identifier of the target or {@code null} + * @param title + * a title to set + * @param params + * parameters to add + * @param props + * properties to add + * @param links + * links to add + * @param parts + * parts to add + * @return {@code true} if the modification was made or {@code false} otherwise + * @throws ProfileResolutionEvaluationException + * if a processing error occurred during profile resolution + */ + public static boolean add( + @NonNull Control control, + @Nullable Position position, + @Nullable String byId, + @Nullable MarkupLine title, + @NonNull List params, + @NonNull List props, + @NonNull List links, + @NonNull List parts) { + return INSTANCE.visitControl( + control, + new Context( + control, + position == null ? Position.ENDING : position, + byId, + title, + params, + props, + links, + parts)); + } + + @Override + public Boolean visitCatalog(Catalog catalog, Context context) { + // not required + throw new UnsupportedOperationException("not needed"); + } + + @Override + public Boolean visitGroup(CatalogGroup group, Context context) { + // not required + throw new UnsupportedOperationException("not needed"); + } + + /** + * If the add applies to the current object, then apply the child objects. + *

+ * An add applies if: + *

    + *
  1. the {@code targetItem} supports all of the children
  2. + *
  3. the context matches if: + *
      + *
    • the target item's id matches the "by-id"; or
    • + *
    • the "by-id" is not defined and the target item is the control matching the target + * context
    • + *
    + *
  4. + *
+ * + * @param + * the type of the {@code targetItem} + * @param targetItem + * the current target to process + * @param titleConsumer + * a consumer to apply a title to or {@code null} if the object has no title field + * @param paramsSupplier + * a supplier for the child {@link Parameter} collection + * @param propsSupplier + * a supplier for the child {@link Property} collection + * @param linksSupplier + * a supplier for the child {@link Link} collection + * @param partsSupplier + * a supplier for the child {@link ControlPart} collection + * @param context + * the add context + * @return {@code true} if a modification was made or {@code false} otherwise + */ + private static boolean handleCurrent( + @NonNull T targetItem, + @Nullable Consumer titleConsumer, + @Nullable Supplier> paramsSupplier, + @Nullable Supplier> propsSupplier, + @Nullable Supplier> linksSupplier, + @Nullable Supplier> partsSupplier, + @NonNull Context context) { + boolean retval = false; + Position position = context.getPosition(); + if (context.appliesTo(targetItem) && !context.isSequenceTargeted(targetItem)) { + // the target item is the target of the add + MarkupLine newTitle = context.getTitle(); + if (newTitle != null) { + assert titleConsumer != null; + titleConsumer.accept(newTitle); + } + + handleCollection(position, context.getParams(), paramsSupplier); + handleCollection(position, context.getProps(), propsSupplier); + handleCollection(position, context.getLinks(), linksSupplier); + handleCollection(position, context.getParts(), partsSupplier); + retval = true; + } + return retval; + } + + private static void handleCollection( + @NonNull Position position, + @NonNull List newItems, + @Nullable Supplier> originalCollectionSupplier) { + if (originalCollectionSupplier != null) { + List oldItems = originalCollectionSupplier.get(); + if (!newItems.isEmpty()) { + if (Position.STARTING.equals(position)) { + oldItems.addAll(0, newItems); + } else { // ENDING + oldItems.addAll(newItems); + } + } + } + } + + // private static void handleChild( + // @NonNull TargetType itemType, + // @NonNull Supplier> collectionSupplier, + // @Nullable Consumer handler, + // @NonNull Context context) { + // boolean handleChildren = !Collections.disjoint(context.getTargetItemTypes(), + // getApplicableTypes(itemType)); + // if (handleChildren && handler != null) { + // // if the child item type is applicable and there is a handler, iterate over children + // Iterator iter = collectionSupplier.get().iterator(); + // while (iter.hasNext()) { + // T item = iter.next(); + // if (item != null) { + // handler.accept(item); + // } + // } + // } + // } + + private static boolean handleChild( + @NonNull TargetType itemType, + @NonNull Supplier> originalCollectionSupplier, + @NonNull Supplier> newItemsSupplier, + @Nullable Function handler, + @NonNull Context context) { + + // determine if this child type can match + boolean isItemTypeMatch = context.isMatchingType(itemType); + + Set applicableTypes = getApplicableTypes(itemType); + boolean descendChild = handler != null && !Collections.disjoint(context.getTargetItemTypes(), applicableTypes); + + boolean retval = false; + if (isItemTypeMatch || descendChild) { + // if the item type is applicable, attempt to match by id + List collection = originalCollectionSupplier.get(); + ListIterator iter = collection.listIterator(); + boolean deferred = false; + while (iter.hasNext()) { + T item = ObjectUtils.requireNonNull(iter.next()); + + if (isItemTypeMatch && context.appliesTo(item) && context.isSequenceTargeted(item)) { + // if id match, inject the new items into the collection + switch (context.getPosition()) { + case AFTER: { + newItemsSupplier.get().forEach(add -> iter.add(add)); + retval = true; + break; + } + case BEFORE: { + iter.previous(); + List adds = newItemsSupplier.get(); + adds.forEach(add -> iter.add(add)); + item = iter.next(); + retval = true; + break; + } + case STARTING: + case ENDING: + deferred = true; + break; + default: + throw new UnsupportedOperationException(context.getPosition().name().toLowerCase(Locale.ROOT)); + } + } + + if (descendChild) { + assert handler != null; + + // handle child items since they are applicable to the search criteria + retval = retval || handler.apply(item); + } + } + + if (deferred) { + List newItems = newItemsSupplier.get(); + if (Position.ENDING.equals(context.getPosition())) { + collection.addAll(newItems); + retval = true; + } else if (Position.STARTING.equals(context.getPosition())) { + collection.addAll(0, newItems); + retval = true; + } + } + } + return retval; + } + + @Override + public Boolean visitControl(Control control, Context context) { + assert context != null; + + if (control.getParams() == null) { + control.setParams(new LinkedList<>()); + } + + if (control.getProps() == null) { + control.setProps(new LinkedList<>()); + } + + if (control.getLinks() == null) { + control.setLinks(new LinkedList<>()); + } + + if (control.getParts() == null) { + control.setParts(new LinkedList<>()); + } + + boolean retval = handleCurrent( + control, + title -> control.setTitle(title), + () -> control.getParams(), + () -> control.getProps(), + () -> control.getLinks(), + () -> control.getParts(), + context); + + // visit params + retval = retval || handleChild( + TargetType.PARAM, + () -> control.getParams(), + () -> context.getParams(), + child -> visitParameter(ObjectUtils.notNull(child), context), + context); + + // visit parts + retval = retval || handleChild( + TargetType.PART, + () -> control.getParts(), + () -> context.getParts(), + child -> visitPart(child, context), + context); + return retval; + } + + @Override + public Boolean visitParameter(Parameter parameter, Context context) { + assert context != null; + if (parameter.getProps() == null) { + parameter.setProps(new LinkedList<>()); + } + + if (parameter.getLinks() == null) { + parameter.setLinks(new LinkedList<>()); + } + + return handleCurrent( + parameter, + null, + null, + () -> parameter.getProps(), + () -> parameter.getLinks(), + null, + context); + } + + public Boolean visitPart(ControlPart part, Context context) { + assert context != null; + if (part.getProps() == null) { + part.setProps(new LinkedList<>()); + } + + if (part.getLinks() == null) { + part.setLinks(new LinkedList<>()); + } + + if (part.getParts() == null) { + part.setParts(new LinkedList<>()); + } + + boolean retval = handleCurrent( + part, + null, + null, + () -> part.getProps(), + () -> part.getLinks(), + () -> part.getParts(), + context); + + // visit parts + retval = retval || handleChild( + TargetType.PART, + () -> part.getParts(), + () -> context.getParts(), + child -> visitPart(child, context), + context); + return retval; + } + + static class Context { + @NonNull + private static final Set TITLE_TYPES = ObjectUtils.notNull( + Set.of(TargetType.CONTROL, TargetType.PART)); + @NonNull + private static final Set PARAM_TYPES = ObjectUtils.notNull( + Set.of(TargetType.CONTROL, TargetType.PARAM)); + @NonNull + private static final Set PROP_TYPES = ObjectUtils.notNull( + Set.of(TargetType.CONTROL, TargetType.PARAM, TargetType.PART)); + @NonNull + private static final Set LINK_TYPES = ObjectUtils.notNull( + Set.of(TargetType.CONTROL, TargetType.PARAM, TargetType.PART)); + @NonNull + private static final Set PART_TYPES = ObjectUtils.notNull( + Set.of(TargetType.CONTROL, TargetType.PART)); + + @NonNull + private final Control control; + @NonNull + private final Position position; + @Nullable + private final String byId; + @Nullable + private final MarkupLine title; + @NonNull + private final List params; + @NonNull + private final List props; + @NonNull + private final List links; + @NonNull + private final List parts; + @NonNull + private final Set targetItemTypes; + + public Context( + @NonNull Control control, + @NonNull Position position, + @Nullable String byId, + @Nullable MarkupLine title, + @NonNull List params, + @NonNull List props, + @NonNull List links, + @NonNull List parts) { + + Set targetItemTypes = ObjectUtils.notNull(EnumSet.allOf(TargetType.class)); + + List additionObjects = new LinkedList<>(); + + boolean sequenceTarget = true; + if (title != null) { + targetItemTypes.retainAll(TITLE_TYPES); + additionObjects.add("title"); + sequenceTarget = false; + } + + if (!params.isEmpty()) { + targetItemTypes.retainAll(PARAM_TYPES); + additionObjects.add("param"); + } + + if (!props.isEmpty()) { + targetItemTypes.retainAll(PROP_TYPES); + additionObjects.add("prop"); + sequenceTarget = false; + } + + if (!links.isEmpty()) { + targetItemTypes.retainAll(LINK_TYPES); + additionObjects.add("link"); + sequenceTarget = false; + } + + if (!parts.isEmpty()) { + targetItemTypes.retainAll(PART_TYPES); + additionObjects.add("part"); + } + + if (Position.BEFORE.equals(position) || Position.AFTER.equals(position)) { + if (sequenceTarget) { + if (sequenceTarget && !params.isEmpty() && parts.isEmpty()) { + targetItemTypes.retainAll(Set.of(TargetType.PARAM)); + } else if (sequenceTarget && !parts.isEmpty() && params.isEmpty()) { + targetItemTypes.retainAll(Set.of(TargetType.PART)); + } else { + throw new ProfileResolutionEvaluationException( + "When using position before or after, only one collection of parameters or parts can be specified."); + } + } else { + throw new ProfileResolutionEvaluationException( + "When using position before or after, one collection of parameters or parts can be specified." + + " Other additions must not be used."); + } + } + + if (targetItemTypes.isEmpty()) { + throw new ProfileResolutionEvaluationException("No parent object supports the requested objects to add: " + + additionObjects.stream().collect(CustomCollectors.joiningWithOxfordComma("or"))); + } + + this.control = control; + this.position = position; + this.byId = byId; + this.title = title; + this.params = params; + this.props = props; + this.links = links; + this.parts = parts; + this.targetItemTypes = CollectionUtil.unmodifiableSet(targetItemTypes); + } + + public Control getControl() { + return control; + } + + @NonNull + public Position getPosition() { + return position; + } + + @Nullable + public String getById() { + return byId; + } + + @Nullable + public MarkupLine getTitle() { + return title; + } + + @NonNull + public List getParams() { + return params; + } + + @NonNull + public List getProps() { + return props; + } + + @NonNull + public List getLinks() { + return links; + } + + @NonNull + public List getParts() { + return parts; + } + + @NonNull + public Set getTargetItemTypes() { + return targetItemTypes; + } + + public boolean isMatchingType(@NonNull TargetType type) { + return getTargetItemTypes().contains(type); + } + + public boolean isSequenceTargeted(T targetItem) { + TargetType objectType = TargetType.forClass(targetItem.getClass()); + return (Position.BEFORE.equals(position) || Position.AFTER.equals(position)) + && (TargetType.PARAM.equals(objectType) && isMatchingType(TargetType.PARAM) + || TargetType.PART.equals(objectType) && isMatchingType(TargetType.PART)); + } + + protected boolean checkValue(@Nullable String actual, @Nullable String expected) { + return expected == null || expected.equals(actual); + } + + /** + * Determine if the provided {@code obj} is the target of the add. + * + * @param obj + * the current object + * @return {@code true} if the current object applies or {@code false} otherwise + */ + public boolean appliesTo(@NonNull Object obj) { + TargetType objectType = TargetType.forClass(obj.getClass()); + + boolean retval = objectType != null && isMatchingType(objectType); + if (retval) { + assert objectType != null; + + // check other criteria + String actualId = null; + + switch (objectType) { + case CONTROL: { + Control control = (Control) obj; + actualId = control.getId(); + break; + } + case PARAM: { + Parameter param = (Parameter) obj; + actualId = param.getId(); + break; + } + case PART: { + ControlPart part = (ControlPart) obj; + actualId = part.getId() == null ? null : part.getId().toString(); + break; + } + default: + throw new UnsupportedOperationException(objectType.fieldName()); + } + + String byId = getById(); + if (getById() == null && TargetType.CONTROL.equals(objectType)) { + retval = getControl().equals(obj); + } else { + retval = byId != null && byId.equals(actualId); + } + } + return retval; + } + } +} diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/RemoveVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/RemoveVisitor.java new file mode 100644 index 00000000..df3d3a72 --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/RemoveVisitor.java @@ -0,0 +1,516 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver.alter; + +import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; +import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; +import gov.nist.secauto.oscal.lib.model.Catalog; +import gov.nist.secauto.oscal.lib.model.CatalogGroup; +import gov.nist.secauto.oscal.lib.model.Control; +import gov.nist.secauto.oscal.lib.model.ControlPart; +import gov.nist.secauto.oscal.lib.model.Link; +import gov.nist.secauto.oscal.lib.model.MappingEntry; +import gov.nist.secauto.oscal.lib.model.Parameter; +import gov.nist.secauto.oscal.lib.model.Property; +import gov.nist.secauto.oscal.lib.model.control.catalog.ICatalogVisitor; +import gov.nist.secauto.oscal.lib.model.metadata.IProperty; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Supplier; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +public class RemoveVisitor implements ICatalogVisitor { + public enum TargetType { + PARAM("param", Parameter.class), + PROP("prop", Property.class), + LINK("link", Link.class), + PART("part", ControlPart.class), + MAPPING("mapping", Control.Mapping.class), + MAP("map", MappingEntry.class); + + @NonNull + private static final Map, TargetType> CLASS_TO_TYPE; + @NonNull + private static final Map NAME_TO_TYPE; + @NonNull + private final String fieldName; + @NonNull + private final Class clazz; + + static { + { + Map, TargetType> map = new ConcurrentHashMap<>(); + for (TargetType type : TargetType.values()) { + map.put(type.getClazz(), type); + } + CLASS_TO_TYPE = CollectionUtil.unmodifiableMap(map); + } + + { + Map map = new ConcurrentHashMap<>(); + for (TargetType type : TargetType.values()) { + map.put(type.fieldName(), type); + } + NAME_TO_TYPE = CollectionUtil.unmodifiableMap(map); + } + } + + @Nullable + public static TargetType forClass(@NonNull Class clazz) { + Class target = clazz; + TargetType retval; + // recurse over parent classes to find a match + do { + retval = CLASS_TO_TYPE.get(target); + } while (retval == null && (target = target.getSuperclass()) != null); + return retval; + } + + @Nullable + public static TargetType forFieldName(@Nullable String name) { + return name == null ? null : NAME_TO_TYPE.get(name); + } + + TargetType(@NonNull String fieldName, @NonNull Class clazz) { + this.fieldName = fieldName; + this.clazz = clazz; + } + + public String fieldName() { + return fieldName; + } + + public Class getClazz() { + return clazz; + } + + } + + @NonNull + private static final RemoveVisitor INSTANCE = new RemoveVisitor(); + + private static final Map> APPLICABLE_TARGETS; + + static { + APPLICABLE_TARGETS = new EnumMap<>(TargetType.class); + APPLICABLE_TARGETS.put(TargetType.PARAM, Set.of(TargetType.PROP, TargetType.LINK)); + APPLICABLE_TARGETS.put(TargetType.PART, Set.of(TargetType.PART, TargetType.PROP, TargetType.LINK)); + APPLICABLE_TARGETS.put(TargetType.MAPPING, Set.of(TargetType.MAP, TargetType.PROP, TargetType.LINK)); + APPLICABLE_TARGETS.put(TargetType.MAP, Set.of(TargetType.PROP, TargetType.LINK)); + } + + private static Set getApplicableTypes(@NonNull TargetType type) { + return APPLICABLE_TARGETS.getOrDefault(type, CollectionUtil.emptySet()); + } + + private static boolean handle( + @NonNull TargetType itemType, + @NonNull Supplier> supplier, + @Nullable Function handler, + @NonNull Context context) { + + boolean handleChildren = !Collections.disjoint(context.getTargetItemTypes(), getApplicableTypes(itemType)); + boolean retval = false; + if (context.isMatchingType(itemType)) { + // if the item type is applicable, attempt to remove any items + Iterator iter = supplier.get().iterator(); + while (iter.hasNext()) { + T item = iter.next(); + + if (item == null || context.appliesTo(item)) { + iter.remove(); + retval = true; + // ignore removed items and their children + } else if (handler != null && handleChildren) { + // handle child items since they are applicable to the search criteria + retval = retval || handler.apply(item); + } + } + } else if (handleChildren && handler != null) { + // if the child item type is applicable and there is a handler, iterate over children + Iterator iter = supplier.get().iterator(); + while (iter.hasNext()) { + T item = iter.next(); + if (item != null) { + retval = retval || handler.apply(item); + } + } + } + return retval; + } + + /** + * Apply the remove directive. + * + * @param control + * the control target + * @param objectName + * the name flag of a matching node to remove + * @param objectClass + * the class flag of a matching node to remove + * @param objectId + * the id flag of a matching node to remove + * @param objectNamespace + * the namespace flag of a matching node to remove + * @param itemType + * the type of a matching node to remove + * @return {@code true} if the modification was made or {@code false} otherwise + * @throws ProfileResolutionEvaluationException + * if a processing error occurred during profile resolution + */ + public static boolean remove( + @NonNull Control control, + @Nullable String objectName, + @Nullable String objectClass, + @Nullable String objectId, + @Nullable String objectNamespace, + @Nullable TargetType itemType) { + return INSTANCE.visitControl( + control, + new Context(objectName, objectClass, objectId, objectNamespace, itemType)); + } + + @Override + public Boolean visitCatalog(Catalog catalog, Context context) { + // not required + throw new UnsupportedOperationException("not needed"); + } + + @Override + public Boolean visitGroup(CatalogGroup group, Context context) { + // not required + throw new UnsupportedOperationException("not needed"); + } + + @Override + public Boolean visitControl(Control control, Context context) { + assert context != null; + + // visit params + boolean retval = handle( + TargetType.PARAM, + () -> CollectionUtil.listOrEmpty(control.getParams()), + child -> visitParameter(ObjectUtils.notNull(child), context), + context); + + // visit props + retval = retval || handle( + TargetType.PROP, + () -> CollectionUtil.listOrEmpty(control.getProps()), + null, + context); + + // visit links + retval = retval || handle( + TargetType.LINK, + () -> CollectionUtil.listOrEmpty(control.getLinks()), + null, + context); + + // visit parts + retval = retval || handle( + TargetType.PART, + () -> CollectionUtil.listOrEmpty(control.getParts()), + child -> visitPart(child, context), + context); + + // visit mappings + Control.Mapping mapping = control.getMapping(); + if (mapping != null) { + retval = retval || visitMapping(mapping, context); + } + return retval; + } + + @Override + public Boolean visitParameter(Parameter parameter, Context context) { + assert context != null; + + // visit props + boolean retval = handle( + TargetType.PROP, + () -> CollectionUtil.listOrEmpty(parameter.getProps()), + null, + context); + + // visit links + retval = retval || handle( + TargetType.LINK, + () -> CollectionUtil.listOrEmpty(parameter.getLinks()), + null, + context); + return retval; + } + + public Boolean visitPart(ControlPart part, Context context) { + assert context != null; + + // visit props + boolean retval = handle( + TargetType.PROP, + () -> CollectionUtil.listOrEmpty(part.getProps()), + null, + context); + + // visit links + retval = retval || handle( + TargetType.LINK, + () -> CollectionUtil.listOrEmpty(part.getLinks()), + null, + context); + + // visit parts + retval = retval || handle( + TargetType.PART, + () -> CollectionUtil.listOrEmpty(part.getParts()), + child -> visitPart(child, context), + context); + return retval; + } + + public Boolean visitMapping(Control.Mapping mapping, Context context) { + assert context != null; + + // visit maps + return handle( + TargetType.MAP, + () -> CollectionUtil.listOrEmpty(mapping.getMaps()), + child -> visitMap(child, context), + context); + } + + public Boolean visitMap(MappingEntry map, Context context) { + assert context != null; + + // visit props + boolean retval = handle( + TargetType.PROP, + () -> CollectionUtil.listOrEmpty(map.getProps()), + null, + context); + + // visit links + retval = retval || handle( + TargetType.LINK, + () -> CollectionUtil.listOrEmpty(map.getLinks()), + null, + context); + return retval; + } + + static class Context { + /** + * Types with an "name" flag. + */ + @NonNull + private static final Set NAME_TYPES = ObjectUtils.notNull( + Set.of(TargetType.PART, TargetType.PROP)); + /** + * Types with an "class" flag. + */ + @NonNull + private static final Set CLASS_TYPES = ObjectUtils.notNull( + Set.of(TargetType.PARAM, TargetType.PART, TargetType.PROP)); + /** + * Types with an "id" flag. + */ + @NonNull + private static final Set ID_TYPES = ObjectUtils.notNull( + Set.of(TargetType.PARAM, TargetType.PART)); + /** + * Types with an "ns" flag. + */ + @NonNull + private static final Set NAMESPACE_TYPES = ObjectUtils.notNull( + Set.of(TargetType.PART, TargetType.PROP)); + + @Nullable + private final String objectName; + @Nullable + private final String objectClass; + @Nullable + private final String objectId; + @Nullable + private final String objectNamespace; + @NonNull + private final Set targetItemTypes; + + private static boolean filterTypes( + @NonNull Set effectiveTypes, + @NonNull String criteria, + @NonNull Set allowedTypes, + @Nullable String value, + @Nullable TargetType itemType) { + boolean retval = false; + if (value != null) { + retval = effectiveTypes.retainAll(allowedTypes); + if (itemType != null && !allowedTypes.contains(itemType)) { + throw new ProfileResolutionEvaluationException( + String.format("%s='%s' is not supported for items of type '%s'", + criteria, + value, + itemType.fieldName())); + } + } + return retval; + } + + private Context( + @Nullable String objectName, + @Nullable String objectClass, + @Nullable String objectId, + @Nullable String objectNamespace, + @Nullable TargetType itemType) { + + // determine the set of effective item types to search for + // this helps with short-circuit searching for parts of the graph that cannot match + @NonNull + Set targetItemTypes = ObjectUtils.notNull(EnumSet.allOf(TargetType.class)); + filterTypes(targetItemTypes, "by-name", NAME_TYPES, objectName, itemType); + filterTypes(targetItemTypes, "by-class", CLASS_TYPES, objectClass, itemType); + filterTypes(targetItemTypes, "by-id", ID_TYPES, objectId, itemType); + filterTypes(targetItemTypes, "by-ns", NAMESPACE_TYPES, objectNamespace, itemType); + + if (itemType != null) { + targetItemTypes.retainAll(Set.of(itemType)); + } + + if (targetItemTypes.isEmpty()) { + throw new ProfileResolutionEvaluationException("The filter matches no available item types"); + } + + this.objectName = objectName; + this.objectClass = objectClass; + this.objectId = objectId; + this.objectNamespace = objectNamespace; + this.targetItemTypes = CollectionUtil.unmodifiableSet(targetItemTypes); + } + + @Nullable + public String getObjectName() { + return objectName; + } + + @Nullable + public String getObjectClass() { + return objectClass; + } + + @Nullable + public String getObjectId() { + return objectId; + } + + @NonNull + public Set getTargetItemTypes() { + return targetItemTypes; + } + + public boolean isMatchingType(@NonNull TargetType type) { + return getTargetItemTypes().contains(type); + } + + @Nullable + public String getObjectNamespace() { + return objectNamespace; + } + + protected boolean checkValue(@Nullable String actual, @Nullable String expected) { + return expected != null && expected.equals(actual); + } + + public boolean appliesTo(@NonNull Object obj) { + TargetType objectType = TargetType.forClass(obj.getClass()); + + boolean retval = objectType != null && getTargetItemTypes().contains(objectType); + if (retval) { + assert objectType != null; + + // check other criteria + String actualName = null; + String actualClass = null; + String actualId = null; + String actualNamespace = null; + + switch (objectType) { + case PARAM: { + Parameter param = (Parameter) obj; + actualClass = param.getClazz(); + actualId = param.getId(); + break; + } + case PROP: { + Property prop = (Property) obj; + actualName = prop.getName(); + actualClass = prop.getClazz(); + actualNamespace = prop.getNs() == null ? IProperty.OSCAL_NAMESPACE.toString() : prop.getNs().toString(); + break; + } + case PART: { + ControlPart part = (ControlPart) obj; + actualName = part.getName(); + actualClass = part.getClazz(); + actualId = part.getId() == null ? null : part.getId().toString(); + actualNamespace = part.getNs() == null ? IProperty.OSCAL_NAMESPACE.toString() : part.getNs().toString(); + break; + } + case LINK: + case MAPPING: + case MAP: + // do nothing + break; + default: + throw new UnsupportedOperationException(objectType.name().toLowerCase(Locale.ROOT)); + } + + retval = checkValue(actualName, getObjectName()); + if (retval) { + checkValue(actualClass, getObjectClass()); + } + if (retval) { + checkValue(actualId, getObjectId()); + } + if (retval) { + checkValue(actualNamespace, getObjectNamespace()); + } + } + return retval; + } + } +} diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/package-info.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/package-info.java new file mode 100644 index 00000000..e774fc7c --- /dev/null +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/alter/package-info.java @@ -0,0 +1,27 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver.alter; \ No newline at end of file diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractCustomReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractCustomReferencePolicy.java index b6f178c0..c025c67f 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractCustomReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractCustomReferencePolicy.java @@ -32,24 +32,25 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.List; +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + public abstract class AbstractCustomReferencePolicy implements ICustomReferencePolicy { private static final Logger LOGGER = LogManager.getLogger(AbstractCustomReferencePolicy.class); - @NotNull + @NonNull private final IIdentifierParser identifierParser; protected AbstractCustomReferencePolicy( - @NotNull IIdentifierParser identifierParser) { + @NonNull IIdentifierParser identifierParser) { this.identifierParser = identifierParser; } @Override - @NotNull + @NonNull public IIdentifierParser getIdentifierParser() { return identifierParser; } @@ -64,13 +65,25 @@ public IIdentifierParser getIdentifierParser() { * the reference object * @return a list of item types to search for */ - @NotNull - protected abstract List<@NotNull ItemType> getEntityItemTypes(@NotNull TYPE reference); + @NonNull + protected abstract List getEntityItemTypes(@NonNull TYPE reference); + /** + * Handle an index hit. + * + * @param reference + * the identifier reference object generating the hit + * @param item + * the referenced item + * @param visitor + * the reference visitor, which can be used for further processing + * @return {@code true} if the hit was handled or {@code false} otherwise + * @throw ProfileResolutionEvaluationException if there was an error handing the index hit + */ protected boolean handleIndexHit( - @NotNull TYPE reference, - @NotNull EntityItem item, - @NotNull IReferenceVisitor visitor) { + @NonNull TYPE reference, + @NonNull EntityItem item, + @NonNull IReferenceVisitor visitor) { if (item.isSelected(visitor.getIndex())) { if (item.getReferenceCount() == 0 && !item.isResolved()) { @@ -95,55 +108,126 @@ protected boolean handleIndexHit( return true; } - protected void handleUnselected( // NOPMD - intentional - @NotNull TYPE reference, - @NotNull EntityItem item, - @NotNull IReferenceVisitor visitor) { + /** + * Handle an index hit against an item related to an unselected control. + *

+ * Subclasses can override this method to perform extra processing. + * + * @param reference + * the identifier reference object generating the hit + * @param item + * the referenced item + * @param visitor + * the reference visitor, which can be used for further processing + * @throw ProfileResolutionEvaluationException if there was an error handing the index hit + */ + protected void handleUnselected( // NOPMD - do nothing by default + @NonNull TYPE reference, + @NonNull EntityItem item, + @NonNull IReferenceVisitor visitor) { // do nothing by default } - protected void handleSelected( // NOPMD - intentional - @NotNull TYPE reference, - @NotNull EntityItem item, - @NotNull IReferenceVisitor visitor) { + /** + * Handle an index hit against an item related to an selected control. + *

+ * Subclasses can override this method to perform extra processing. + * + * @param reference + * the identifier reference object generating the hit + * @param item + * the referenced item + * @param visitor + * the reference visitor, which can be used for further processing + * @throw ProfileResolutionEvaluationException if there was an error handing the index hit + */ + protected void handleSelected( // NOPMD - do nothing by default + @NonNull TYPE reference, + @NonNull EntityItem item, + @NonNull IReferenceVisitor visitor) { // do nothing by default } + /** + * Handle an index miss for a reference. This occurs when the referenced item was not found in the + * index. + *

+ * Subclasses can override this method to perform extra processing. + * + * @param reference + * the identifier reference object generating the hit + * @param itemTypes + * the possible item types for this reference + * @param identifier + * the parsed identifier + * @param visitor + * the reference visitor, which can be used for further processing + * @return {@code true} if the reference is handled by this method or {@code false} otherwise + * @throw ProfileResolutionEvaluationException if there was an error handing the index miss + */ protected boolean handleIndexMiss( - @NotNull TYPE reference, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull TYPE reference, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor) { // provide no handler by default return false; } + /** + * Handle the case where the identifier was not a syntax match for an expected identifier. This can + * occur when the reference is malformed, using an unrecognized syntax. + *

+ * Subclasses can override this method to perform extra processing. + * + * @param reference + * the identifier reference object generating the hit + * @param visitor + * the reference visitor, which can be used for further processing + * @return {@code true} if the reference is handled by this method or {@code false} otherwise + * @throw ProfileResolutionEvaluationException if there was an error handing the index miss due to a + * non match + */ protected boolean handleIdentifierNonMatch( - @NotNull TYPE reference, - @NotNull IReferenceVisitor visitor) { + @NonNull TYPE reference, + @NonNull IReferenceVisitor visitor) { // provide no handler by default return false; } @Override - public boolean handleReference(@NotNull TYPE type, @NotNull IReferenceVisitor visitor) { + public boolean handleReference(@NonNull TYPE type, @NonNull IReferenceVisitor visitor) { String referenceText = getReferenceText(type); // if the reference text does not exist, ignore the reference; otherwise, handle it. return referenceText == null || handleIdentifier(type, getIdentifierParser().parse(referenceText), visitor); } + /** + * Handle the provided {@code identifier} for a given {@code type} of reference. + * + * @param type + * the item type of the reference + * @param identifier + * the identifier + * @param visitor + * the reference visitor, which can be used for further processing + * @return {@code true} if the reference is handled by this method or {@code false} otherwise + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ protected boolean handleIdentifier( - @NotNull TYPE type, + @NonNull TYPE type, @Nullable String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull IReferenceVisitor visitor) { boolean retval; if (identifier == null) { retval = handleIdentifierNonMatch(type, visitor); } else { - List<@NotNull ItemType> itemTypes = getEntityItemTypes(type); + List itemTypes = getEntityItemTypes(type); EntityItem item = null; for (ItemType itemType : itemTypes) { + assert itemType != null; + item = visitor.getIndex().getEntity(itemType, identifier); if (item != null) { break; diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractIndexMissPolicyHandler.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractIndexMissPolicyHandler.java index 5d5e829b..5c910dd1 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractIndexMissPolicyHandler.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractIndexMissPolicyHandler.java @@ -28,16 +28,16 @@ import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; public abstract class AbstractIndexMissPolicyHandler implements ICustomReferencePolicyHandler { @Override public abstract boolean handleIndexMiss( - @NotNull ICustomReferencePolicy policy, - @NotNull TYPE type, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor); + @NonNull ICustomReferencePolicy policy, + @NonNull TYPE type, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractMultiItemTypeReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractMultiItemTypeReferencePolicy.java index c3ded14a..fd3c192b 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractMultiItemTypeReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AbstractMultiItemTypeReferencePolicy.java @@ -29,25 +29,25 @@ import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; public abstract class AbstractMultiItemTypeReferencePolicy extends AbstractCustomReferencePolicy { - @NotNull - private final List<@NotNull ItemType> itemTypes; + @NonNull + private final List itemTypes; public AbstractMultiItemTypeReferencePolicy( - @NotNull IIdentifierParser identifierParser, - @NotNull List<@NotNull ItemType> itemTypes) { + @NonNull IIdentifierParser identifierParser, + @NonNull List itemTypes) { super(identifierParser); this.itemTypes = CollectionUtil.requireNonEmpty(itemTypes, "itemTypes"); } @Override - protected List<@NotNull ItemType> getEntityItemTypes(@NotNull TYPE type) { + protected List getEntityItemTypes(@NonNull TYPE type) { return itemTypes; } } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AnchorReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AnchorReferencePolicy.java index ecbae428..36c2f55e 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AnchorReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/AnchorReferencePolicy.java @@ -36,12 +36,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.net.URI; import java.util.List; import java.util.Locale; +import edu.umd.cs.findbugs.annotations.NonNull; + public class AnchorReferencePolicy extends AbstractCustomReferencePolicy { private static final Logger LOGGER = LogManager.getLogger(AnchorReferencePolicy.class); @@ -52,23 +53,23 @@ public AnchorReferencePolicy() { @SuppressWarnings("null") @Override - protected List<@NotNull ItemType> getEntityItemTypes(@NotNull InlineLinkNode link) { + protected List getEntityItemTypes(@NonNull InlineLinkNode link) { return List.of(ItemType.RESOURCE, ItemType.CONTROL, ItemType.GROUP, ItemType.PART); } @Override - public String getReferenceText(@NotNull InlineLinkNode link) { + public String getReferenceText(@NonNull InlineLinkNode link) { return link.getUrl().toString(); } @Override - public void setReferenceText(@NotNull InlineLinkNode link, @NotNull String newValue) { + public void setReferenceText(@NonNull InlineLinkNode link, @NonNull String newValue) { link.setUrl(BasedSequence.of(newValue)); } @Override - protected void handleUnselected(@NotNull InlineLinkNode link, @NotNull EntityItem item, - @NotNull IReferenceVisitor visitor) { + protected void handleUnselected(@NonNull InlineLinkNode link, @NonNull EntityItem item, + @NonNull IReferenceVisitor visitor) { URI linkHref = URI.create(link.getUrl().toString()); URI sourceUri = item.getSource(); @@ -81,10 +82,10 @@ protected void handleUnselected(@NotNull InlineLinkNode link, @NotNull EntityIte @Override protected boolean handleIndexMiss( - @NotNull InlineLinkNode reference, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull InlineLinkNode reference, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor) { if (LOGGER.isErrorEnabled()) { LOGGER.atError().log( "the anchor should reference a {} identified by '{}', but the identifier was not found in the index.", @@ -97,7 +98,7 @@ protected boolean handleIndexMiss( } @Override - protected boolean handleIdentifierNonMatch(@NotNull InlineLinkNode reference, @NotNull IReferenceVisitor visitor) { + protected boolean handleIdentifierNonMatch(@NonNull InlineLinkNode reference, @NonNull IReferenceVisitor visitor) { if (LOGGER.isDebugEnabled()) { LOGGER.atDebug().log("Ignoring URI '{}'", reference.getUrl().toStringOrNull()); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicy.java index abc3ec7a..037f654d 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicy.java @@ -26,7 +26,7 @@ package gov.nist.secauto.oscal.lib.profile.resolver.policy; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; public interface ICustomReferencePolicy extends IReferencePolicy { @@ -35,7 +35,7 @@ public interface ICustomReferencePolicy extends IReferencePolicy { * * @return the parser */ - @NotNull + @NonNull IIdentifierParser getIdentifierParser(); /** @@ -45,7 +45,7 @@ public interface ICustomReferencePolicy extends IReferencePolicy { * the reference object * @return the reference text or {@code null} if there is no text */ - String getReferenceText(@NotNull TYPE reference); + String getReferenceText(@NonNull TYPE reference); /** * Update the reference text used in the {@code reference} object. @@ -55,5 +55,5 @@ public interface ICustomReferencePolicy extends IReferencePolicy { * @param newReferenceText * the reference text replacement */ - void setReferenceText(@NotNull TYPE reference, @NotNull String newReferenceText); + void setReferenceText(@NonNull TYPE reference, @NonNull String newReferenceText); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicyHandler.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicyHandler.java index 2e1beeb6..d9845025 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicyHandler.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ICustomReferencePolicyHandler.java @@ -29,20 +29,20 @@ import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem; import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import java.util.List; public interface ICustomReferencePolicyHandler { - @NotNull + @NonNull ICustomReferencePolicyHandler IGNORE_INDEX_MISS_POLICY = new AbstractIndexMissPolicyHandler<>() { @Override public boolean handleIndexMiss( - @NotNull ICustomReferencePolicy policy, - @NotNull Object type, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull ICustomReferencePolicy policy, + @NonNull Object type, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor) { // do nothing return true; } @@ -61,9 +61,9 @@ public boolean handleIndexMiss( * @return {@code true} if the reference is considered handled, or {@code false} otherwise */ default boolean handleIdentifierNonMatch( - @NotNull ICustomReferencePolicy policy, - @NotNull TYPE reference, - @NotNull IReferenceVisitor visitor) { + @NonNull ICustomReferencePolicy policy, + @NonNull TYPE reference, + @NonNull IReferenceVisitor visitor) { return false; } @@ -84,11 +84,11 @@ default boolean handleIdentifierNonMatch( * @return {@code true} if the reference is considered handled, or {@code false} otherwise */ default boolean handleIndexMiss( - @NotNull ICustomReferencePolicy policy, - @NotNull TYPE reference, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull ICustomReferencePolicy policy, + @NonNull TYPE reference, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor) { return false; } @@ -107,10 +107,10 @@ default boolean handleIndexMiss( * @return {@code true} if the reference is considered handled, or {@code false} otherwise */ default boolean handleIndexHit( - @NotNull ICustomReferencePolicy policy, - @NotNull TYPE reference, - @NotNull EntityItem item, - @NotNull IReferenceVisitor visitor) { + @NonNull ICustomReferencePolicy policy, + @NonNull TYPE reference, + @NonNull EntityItem item, + @NonNull IReferenceVisitor visitor) { return false; } } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IIdentifierParser.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IIdentifierParser.java index 9e35e231..bdf06b20 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IIdentifierParser.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IIdentifierParser.java @@ -26,22 +26,24 @@ package gov.nist.secauto.oscal.lib.profile.resolver.policy; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; public interface IIdentifierParser { - @NotNull + @NonNull IIdentifierParser FRAGMENT_PARSER = new PatternIdentifierParser("^#([^#]+)(?:#.*)?$", 1); - @NotNull + @NonNull IIdentifierParser IDENTITY_PARSER = new IIdentifierParser() { @Override - public String parse(@NotNull String reference) { + public String parse(@NonNull String reference) { return reference; } @Override - public String update(@NotNull String reference, @NotNull String newIdentifier) { + public String update(@NonNull String reference, @NonNull String newIdentifier) { return newIdentifier; } }; @@ -54,7 +56,7 @@ public String update(@NotNull String reference, @NotNull String newIdentifier) { * @return the identifier, or {@code null} if the identifier could not be parsed */ @Nullable - String parse(@NotNull String referenceText); + String parse(@NonNull String referenceText); /** * Substitute the provided {@code newIdentifier} with the identifier in the {@code referenceText}. @@ -64,7 +66,8 @@ public String update(@NotNull String reference, @NotNull String newIdentifier) { * @param newIdentifier * the new identifier to replace the existing identifier * @return the updated reference text with the identifier replaced + * @throws ProfileResolutionEvaluationException if the identifier could not be updated */ - @NotNull - String update(@NotNull String referenceText, @NotNull String newIdentifier); + @NonNull + String update(@NonNull String referenceText, @NonNull String newIdentifier); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferencePolicy.java index 824afa5b..34e441ad 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferencePolicy.java @@ -26,14 +26,14 @@ package gov.nist.secauto.oscal.lib.profile.resolver.policy; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; public interface IReferencePolicy { - @NotNull + @NonNull IReferencePolicy IGNORE_POLICY = new IReferencePolicy<>() { @Override - public boolean handleReference(@NotNull Object reference, @NotNull IReferenceVisitor visitor) { + public boolean handleReference(@NonNull Object reference, @NonNull IReferenceVisitor visitor) { return true; } }; @@ -46,9 +46,9 @@ public boolean handleReference(@NotNull Object reference, @NotNull IReferenceVis * @return the policy */ @SuppressWarnings("unchecked") - @NotNull + @NonNull static IReferencePolicy ignore() { - return (@NotNull IReferencePolicy) IGNORE_POLICY; + return (IReferencePolicy) IGNORE_POLICY; } /** @@ -59,6 +59,7 @@ static IReferencePolicy ignore() { * @param visitor * used to lookup and resolve items * @return {@code true} if the reference was handled, or {@code false} otherwise + * @throw ProfileResolutionEvaluationException if there was an error handing the reference */ - boolean handleReference(@NotNull T reference, @NotNull IReferenceVisitor visitor); + boolean handleReference(@NonNull T reference, @NonNull IReferenceVisitor visitor); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferenceVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferenceVisitor.java index 8eea98da..dd90109e 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferenceVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/IReferenceVisitor.java @@ -27,27 +27,100 @@ package gov.nist.secauto.oscal.lib.profile.resolver.policy; import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; +import gov.nist.secauto.oscal.lib.model.BackMatter.Resource; +import gov.nist.secauto.oscal.lib.model.CatalogGroup; +import gov.nist.secauto.oscal.lib.model.Control; +import gov.nist.secauto.oscal.lib.model.ControlPart; +import gov.nist.secauto.oscal.lib.model.Location; +import gov.nist.secauto.oscal.lib.model.Parameter; +import gov.nist.secauto.oscal.lib.model.Party; +import gov.nist.secauto.oscal.lib.model.Role; import gov.nist.secauto.oscal.lib.profile.resolver.Index; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; public interface IReferenceVisitor { - @NotNull + @NonNull Index getIndex(); - void visitGroup(@NotNull IRequiredValueModelNodeItem item); + /** + * Visit the provided {@code item} representing an OSCAL {@link CatalogGroup} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitGroup(@NonNull IRequiredValueModelNodeItem item); - void visitControl(@NotNull IRequiredValueModelNodeItem item); + /** + * Visit the provided {@code item} representing an OSCAL {@link Control} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitControl(@NonNull IRequiredValueModelNodeItem item); - void visitParameter(@NotNull IRequiredValueModelNodeItem item); + /** + * Visit the provided {@code item} representing an OSCAL {@link Parameter} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitParameter(@NonNull IRequiredValueModelNodeItem item); - void visitPart(@NotNull IRequiredValueModelNodeItem item); - void visitRole(@NotNull IRequiredValueModelNodeItem item); + /** + * Visit the provided {@code item} representing an OSCAL {@link ControlPart} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitPart(@NonNull IRequiredValueModelNodeItem item); - void visitParty(@NotNull IRequiredValueModelNodeItem item); + /** + * Visit the provided {@code item} representing an OSCAL {@link Role} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitRole(@NonNull IRequiredValueModelNodeItem item); - void visitLocation(@NotNull IRequiredValueModelNodeItem item); + /** + * Visit the provided {@code item} representing an OSCAL {@link Party} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitParty(@NonNull IRequiredValueModelNodeItem item); - void visitResource(@NotNull IRequiredValueModelNodeItem item); + /** + * Visit the provided {@code item} representing an OSCAL {@link Location} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitLocation(@NonNull IRequiredValueModelNodeItem item); + + /** + * Visit the provided {@code item} representing an OSCAL {@link Resource} and handle any + * enclosed references. + * + * @param item + * the Metapath node item containing reference nodes + * @throw ProfileResolutionEvaluationException if there was an error handing the reference + */ + void visitResource(@NonNull IRequiredValueModelNodeItem item); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/InsertReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/InsertReferencePolicy.java index ba2e851f..10198f70 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/InsertReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/InsertReferencePolicy.java @@ -35,11 +35,12 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Locale; +import edu.umd.cs.findbugs.annotations.NonNull; + public class InsertReferencePolicy extends AbstractCustomReferencePolicy { private static final Logger LOGGER = LogManager.getLogger(InsertReferencePolicy.class); @@ -49,10 +50,10 @@ public InsertReferencePolicy() { } @Override - protected List<@NotNull ItemType> getEntityItemTypes(@NotNull InsertAnchorNode insert) { + protected List getEntityItemTypes(@NonNull InsertAnchorNode insert) { String type = insert.getType().toString(); - List<@NotNull ItemType> itemTypes; + List itemTypes; if ("param".equals(type)) { itemTypes = CollectionUtil.singletonList(ItemType.PARAMETER); } else { @@ -62,21 +63,21 @@ public InsertReferencePolicy() { } @Override - public String getReferenceText(@NotNull InsertAnchorNode insert) { + public String getReferenceText(@NonNull InsertAnchorNode insert) { return insert.getIdReference().toString(); } @Override - public void setReferenceText(@NotNull InsertAnchorNode insert, @NotNull String newReference) { + public void setReferenceText(@NonNull InsertAnchorNode insert, @NonNull String newReference) { insert.setIdReference(BasedSequence.of(newReference)); } @Override protected boolean handleIndexMiss( - @NotNull InsertAnchorNode insert, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull InsertAnchorNode insert, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor) { if (LOGGER.isErrorEnabled()) { LOGGER.atError().log( "the '{}' insert should reference a '{}' identified by '{}'. The index did not contain the identifier.", diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/LinkReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/LinkReferencePolicy.java index bc8a8757..2c2e8e88 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/LinkReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/LinkReferencePolicy.java @@ -34,46 +34,47 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.net.URI; import java.util.List; import java.util.Locale; +import edu.umd.cs.findbugs.annotations.NonNull; + public class LinkReferencePolicy extends AbstractMultiItemTypeReferencePolicy { private static final Logger LOGGER = LogManager.getLogger(LinkReferencePolicy.class); @SuppressWarnings("null") - @NotNull - public static LinkReferencePolicy create(@NotNull ItemType itemType) { + @NonNull + public static LinkReferencePolicy create(@NonNull ItemType itemType) { return create(List.of(itemType)); } - @NotNull - public static LinkReferencePolicy create(@NotNull List<@NotNull ItemType> itemTypes) { + @NonNull + public static LinkReferencePolicy create(@NonNull List itemTypes) { return new LinkReferencePolicy(CollectionUtil.requireNonEmpty(itemTypes, "itemTypes")); } - public LinkReferencePolicy(@NotNull List<@NotNull ItemType> itemTypes) { + public LinkReferencePolicy(@NonNull List itemTypes) { super(IIdentifierParser.FRAGMENT_PARSER, itemTypes); } @Override - public String getReferenceText(@NotNull Link link) { + public String getReferenceText(@NonNull Link link) { return link.getHref().toString(); } @Override - public void setReferenceText(@NotNull Link link, @NotNull String newValue) { + public void setReferenceText(@NonNull Link link, @NonNull String newValue) { link.setHref(URI.create(newValue)); } @Override protected void handleUnselected( - @NotNull Link link, - @NotNull EntityItem item, - @NotNull IReferenceVisitor visitor) { + @NonNull Link link, + @NonNull EntityItem item, + @NonNull IReferenceVisitor visitor) { URI linkHref = link.getHref(); URI sourceUri = item.getSource(); @@ -86,10 +87,10 @@ protected void handleUnselected( @Override protected boolean handleIndexMiss( - @NotNull Link link, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull Link link, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor) { if (LOGGER.isWarnEnabled()) { LOGGER.atWarn().log( "link with rel '{}' should reference a {} identified by '{}'. The index did not contain the identifier.", @@ -103,7 +104,7 @@ protected boolean handleIndexMiss( } @Override - protected boolean handleIdentifierNonMatch(@NotNull Link reference, @NotNull IReferenceVisitor visitor) { + protected boolean handleIdentifierNonMatch(@NonNull Link reference, @NonNull IReferenceVisitor visitor) { if (LOGGER.isDebugEnabled()) { LOGGER.atDebug().log("Ignoring URI '{}'", reference.getHref().toString()); } diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PatternIdentifierParser.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PatternIdentifierParser.java index 892d4a18..fdf6687b 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PatternIdentifierParser.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PatternIdentifierParser.java @@ -27,24 +27,24 @@ package gov.nist.secauto.oscal.lib.profile.resolver.policy; import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; - -import org.jetbrains.annotations.NotNull; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionEvaluationException; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; +import edu.umd.cs.findbugs.annotations.NonNull; + public class PatternIdentifierParser implements IIdentifierParser { private final Pattern pattern; private final int identifierGroup; @SuppressWarnings("null") - public PatternIdentifierParser(@NotNull String pattern, int identifierGroup) { + public PatternIdentifierParser(@NonNull String pattern, int identifierGroup) { this(Pattern.compile(pattern), identifierGroup); } - @SuppressWarnings("null") - public PatternIdentifierParser(@NotNull Pattern pattern, int identifierGroup) { + public PatternIdentifierParser(@NonNull Pattern pattern, int identifierGroup) { this.pattern = Objects.requireNonNull(pattern, "pattern"); this.identifierGroup = identifierGroup; } @@ -58,18 +58,23 @@ public int getIdentifierGroup() { } @Override - public String parse(@NotNull String referenceText) { + public String parse(@NonNull String referenceText) { Matcher matcher = getPattern().matcher(referenceText); - return matcher.matches() ? matcher.group(getIdentifierGroup()) : null; + String retval = null; + if (matcher.matches()) { + retval = matcher.group(getIdentifierGroup()); + } + return retval; } @Override - public String update(@NotNull String referenceText, @NotNull String newIdentifier) { + public String update(@NonNull String referenceText, @NonNull String newIdentifier) { Matcher matcher = getPattern().matcher(referenceText); if (!matcher.matches()) { - throw new IllegalStateException(String.format("The original reference '%s' did not match the pattern '%s'.", - referenceText, getPattern().pattern())); + throw new ProfileResolutionEvaluationException( + String.format("The original reference '%s' did not match the pattern '%s'.", + referenceText, getPattern().pattern())); } return ObjectUtils.notNull(new StringBuilder(referenceText) diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PropertyReferencePolicy.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PropertyReferencePolicy.java index 72cba330..3c0d8c48 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PropertyReferencePolicy.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/PropertyReferencePolicy.java @@ -33,49 +33,48 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.net.URI; import java.util.List; import java.util.Locale; +import edu.umd.cs.findbugs.annotations.NonNull; + public class PropertyReferencePolicy extends AbstractMultiItemTypeReferencePolicy { private static final Logger LOGGER = LogManager.getLogger(PropertyReferencePolicy.class); - @SuppressWarnings("null") - @NotNull - public static PropertyReferencePolicy create(@NotNull IIdentifierParser identifierParser, - @NotNull ItemType itemType) { + @NonNull + public static PropertyReferencePolicy create(@NonNull IIdentifierParser identifierParser, + @NonNull ItemType itemType) { return create(identifierParser, List.of(itemType)); } - @NotNull - public static PropertyReferencePolicy create(@NotNull IIdentifierParser identifierParser, - @NotNull List itemTypes) { + @NonNull + public static PropertyReferencePolicy create(@NonNull IIdentifierParser identifierParser, + @NonNull List itemTypes) { return new PropertyReferencePolicy(identifierParser, itemTypes); } - @SuppressWarnings("null") - public PropertyReferencePolicy(@NotNull IIdentifierParser identifierParser, @NotNull List itemTypes) { + public PropertyReferencePolicy(@NonNull IIdentifierParser identifierParser, @NonNull List itemTypes) { super(identifierParser, itemTypes); } @Override - public String getReferenceText(@NotNull Property property) { + public String getReferenceText(@NonNull Property property) { return property.getValue(); } @Override - public void setReferenceText(@NotNull Property property, @NotNull String newValue) { + public void setReferenceText(@NonNull Property property, @NonNull String newValue) { property.setValue(newValue); } @Override protected void handleUnselected( - @NotNull Property property, - @NotNull EntityItem item, - @NotNull IReferenceVisitor visitor) { + @NonNull Property property, + @NonNull EntityItem item, + @NonNull IReferenceVisitor visitor) { URI linkHref = URI.create(property.getValue()); URI sourceUri = item.getSource(); @@ -88,10 +87,10 @@ protected void handleUnselected( @Override protected boolean handleIndexMiss( - @NotNull Property property, - @NotNull List<@NotNull ItemType> itemTypes, - @NotNull String identifier, - @NotNull IReferenceVisitor visitor) { + @NonNull Property property, + @NonNull List itemTypes, + @NonNull String identifier, + @NonNull IReferenceVisitor visitor) { if (LOGGER.isWarnEnabled()) { LOGGER.atWarn().log( "property '{}' should reference a {} identified by '{}', but the identifier was not found in the index.", diff --git a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitor.java b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitor.java index 2a4aeb45..5502dc22 100644 --- a/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitor.java +++ b/src/main/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitor.java @@ -31,13 +31,10 @@ import gov.nist.secauto.metaschema.model.common.datatype.markup.IMarkupText; import gov.nist.secauto.metaschema.model.common.datatype.markup.flexmark.InsertAnchorNode; -import gov.nist.secauto.metaschema.model.common.metapath.ISequence; import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression; -import gov.nist.secauto.metaschema.model.common.metapath.MetapathExpression.ResultType; import gov.nist.secauto.metaschema.model.common.metapath.function.library.FnData; import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; import gov.nist.secauto.metaschema.model.common.metapath.item.IMarkupItem; -import gov.nist.secauto.metaschema.model.common.metapath.item.INodeItem; import gov.nist.secauto.metaschema.model.common.metapath.item.IRequiredValueModelNodeItem; import gov.nist.secauto.metaschema.model.common.metapath.item.IRootAssemblyNodeItem; import gov.nist.secauto.metaschema.model.common.util.CollectionUtil; @@ -52,68 +49,71 @@ import gov.nist.secauto.oscal.lib.model.Party; import gov.nist.secauto.oscal.lib.model.Property; import gov.nist.secauto.oscal.lib.model.Role; +import gov.nist.secauto.oscal.lib.model.metadata.AbstractProperty; +import gov.nist.secauto.oscal.lib.model.metadata.IProperty; import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem; import gov.nist.secauto.oscal.lib.profile.resolver.EntityItem.ItemType; import gov.nist.secauto.oscal.lib.profile.resolver.Index; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.net.URI; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; import javax.xml.namespace.QName; -public class ReferenceCountingVisitor implements IReferenceVisitor { +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +public class ReferenceCountingVisitor implements IReferenceVisitor { // NOPMD - ok private static final Logger LOGGER = LogManager.getLogger(ReferenceCountingVisitor.class); - @NotNull + @NonNull private static final MetapathExpression PART_METAPATH = MetapathExpression.compile("part|part//part"); - @NotNull + @NonNull private static final MetapathExpression PARAM_MARKUP_METAPATH = MetapathExpression .compile("label|usage|constraint/(description|tests/remarks)|guideline/prose|select/choice|remarks"); - @NotNull + @NonNull private static final MetapathExpression ROLE_MARKUP_METAPATH = MetapathExpression.compile("title|description|remarks"); - @NotNull + @NonNull private static final MetapathExpression LOCATION_MARKUP_METAPATH = MetapathExpression.compile("title|remarks"); - @NotNull + @NonNull private static final MetapathExpression PARTY_MARKUP_METAPATH = MetapathExpression.compile("title|remarks"); - @NotNull + @NonNull private static final MetapathExpression RESOURCE_MARKUP_METAPATH = MetapathExpression.compile("title|description|remarks"); - @NotNull + @NonNull private static final IReferencePolicy PROPERTY_POLICY_IGNORE = IReferencePolicy.ignore(); - @NotNull + @NonNull private static final IReferencePolicy LINK_POLICY_IGNORE = IReferencePolicy.ignore(); - @NotNull + @NonNull private static final Map> PROPERTY_POLICIES; - @NotNull + @NonNull private static final Map> LINK_POLICIES; - @NotNull + @NonNull private static final InsertReferencePolicy INSERT_POLICY = new InsertReferencePolicy(); - @NotNull + @NonNull private static final AnchorReferencePolicy ANCHOR_POLICY = new AnchorReferencePolicy(); static { PROPERTY_POLICIES = new HashMap<>(); - PROPERTY_POLICIES.put(Property.qname(Property.OSCAL_NAMESPACE, "resolution-tool"), PROPERTY_POLICY_IGNORE); - PROPERTY_POLICIES.put(Property.qname(Property.OSCAL_NAMESPACE, "label"), PROPERTY_POLICY_IGNORE); - PROPERTY_POLICIES.put(Property.qname(Property.OSCAL_NAMESPACE, "sort-id"), PROPERTY_POLICY_IGNORE); - PROPERTY_POLICIES.put(Property.qname(Property.OSCAL_NAMESPACE, "alt-label"), PROPERTY_POLICY_IGNORE); - PROPERTY_POLICIES.put(Property.qname(Property.OSCAL_NAMESPACE, "alt-identifier"), PROPERTY_POLICY_IGNORE); - PROPERTY_POLICIES.put(Property.qname(Property.RMF_NAMESPACE, "method"), PROPERTY_POLICY_IGNORE); - PROPERTY_POLICIES.put(Property.qname(Property.RMF_NAMESPACE, "aggregates"), + PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "resolution-tool"), PROPERTY_POLICY_IGNORE); + PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "label"), PROPERTY_POLICY_IGNORE); + PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "sort-id"), PROPERTY_POLICY_IGNORE); + PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "alt-label"), PROPERTY_POLICY_IGNORE); + PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.OSCAL_NAMESPACE, "alt-identifier"), PROPERTY_POLICY_IGNORE); + PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.RMF_NAMESPACE, "method"), PROPERTY_POLICY_IGNORE); + PROPERTY_POLICIES.put(AbstractProperty.qname(IProperty.RMF_NAMESPACE, "aggregates"), PropertyReferencePolicy.create(IIdentifierParser.IDENTITY_PARSER, ItemType.PARAMETER)); LINK_POLICIES = new HashMap<>(); @@ -124,29 +124,31 @@ public class ReferenceCountingVisitor implements IReferenceVisitor { LINK_POLICIES.put("required", LinkReferencePolicy.create(ItemType.CONTROL)); } - @NotNull + @NonNull private final Index index; - @NotNull + @NonNull private final URI source; - public ReferenceCountingVisitor(@NotNull Index index, @NotNull URI source) { + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to store this parameter") + public ReferenceCountingVisitor(@NonNull Index index, @NonNull URI source) { this.index = index; this.source = source; } @Override - @NotNull + @NonNull + @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "intending to expose this field") public Index getIndex() { return index; } - @NotNull + @NonNull protected URI getSource() { return source; } // - // public void visitProfile(@NotNull Profile profile) { + // public void visitProfile(@NonNull Profile profile) { // // process children // Metadata metadata = profile.getMetadata(); // if (metadata != null) { @@ -161,26 +163,26 @@ protected URI getSource() { // } // } - public void visitCatalog(@NotNull IDocumentNodeItem catalogItem) { + public void visitCatalog(@NonNull IDocumentNodeItem catalogItem) { // process children IRootAssemblyNodeItem rootItem = catalogItem.getRootAssemblyNodeItem(); rootItem.getModelItemsByName("group").forEach(groupItem -> { - visitGroup(groupItem); + visitGroup(ObjectUtils.notNull(groupItem)); }); rootItem.getModelItemsByName("control").forEach(controlItem -> { - visitControl(controlItem); + visitControl(ObjectUtils.notNull(controlItem)); }); - index.getEntitiesByItemType(ItemType.ROLE).forEach(item -> resolveItem(item)); - index.getEntitiesByItemType(ItemType.LOCATION).forEach(item -> resolveItem(item)); - index.getEntitiesByItemType(ItemType.PARTY).forEach(item -> resolveItem(item)); - index.getEntitiesByItemType(ItemType.PARAMETER).forEach(item -> resolveItem(item)); - index.getEntitiesByItemType(ItemType.RESOURCE).forEach(item -> resolveItem(item)); + index.getEntitiesByItemType(ItemType.ROLE).forEach(item -> resolveItem(ObjectUtils.notNull(item))); + index.getEntitiesByItemType(ItemType.LOCATION).forEach(item -> resolveItem(ObjectUtils.notNull(item))); + index.getEntitiesByItemType(ItemType.PARTY).forEach(item -> resolveItem(ObjectUtils.notNull(item))); + index.getEntitiesByItemType(ItemType.PARAMETER).forEach(item -> resolveItem(ObjectUtils.notNull(item))); + index.getEntitiesByItemType(ItemType.RESOURCE).forEach(item -> resolveItem(ObjectUtils.notNull(item))); } - private void resolveItem(EntityItem item) { + private void resolveItem(@NonNull EntityItem item) { if (LOGGER.isDebugEnabled()) { LOGGER.atDebug().log("Resolving {} identified as '{}'", item.getItemType().name(), item.getIdentifier()); } @@ -190,91 +192,91 @@ private void resolveItem(EntityItem item) { } @Override - public void visitRole(@NotNull IRequiredValueModelNodeItem item) { + public void visitRole(@NonNull IRequiredValueModelNodeItem item) { Role role = (Role) item.getValue(); EntityItem entity = getIndex().getEntity(ItemType.ROLE, ObjectUtils.notNull(role.getId())); if (!entity.isResolved()) { entity.markResolved(); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); - item.getModelItemsByName("link").forEach(child -> handleLink(child)); - evaluateToList(ROLE_MARKUP_METAPATH, item).forEach(child -> handleMarkup(child)); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); + item.getModelItemsByName("link").forEach(child -> handleLink(ObjectUtils.notNull(child))); + ROLE_MARKUP_METAPATH.evaluate(item).asList() + .forEach(child -> handleMarkup(ObjectUtils.notNull((IRequiredValueModelNodeItem) child))); } } @Override - public void visitParty(@NotNull IRequiredValueModelNodeItem item) { + public void visitParty(@NonNull IRequiredValueModelNodeItem item) { Party party = (Party) item.getValue(); EntityItem entity = getIndex().getEntity(ItemType.PARTY, ObjectUtils.notNull(party.getUuid())); if (!entity.isResolved()) { entity.markResolved(); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); - item.getModelItemsByName("link").forEach(child -> handleLink(child)); - evaluateToList(PARTY_MARKUP_METAPATH, item).forEach(child -> handleMarkup(child)); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); + item.getModelItemsByName("link").forEach(child -> handleLink(ObjectUtils.notNull(child))); + PARTY_MARKUP_METAPATH.evaluate(item).asList() + .forEach(child -> handleMarkup(ObjectUtils.notNull((IRequiredValueModelNodeItem) child))); } } @Override - public void visitLocation(@NotNull IRequiredValueModelNodeItem item) { + public void visitLocation(@NonNull IRequiredValueModelNodeItem item) { Location location = (Location) item.getValue(); EntityItem entity = getIndex().getEntity(ItemType.LOCATION, ObjectUtils.notNull(location.getUuid())); if (!entity.isResolved()) { entity.markResolved(); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); - item.getModelItemsByName("link").forEach(child -> handleLink(child)); - evaluateToList(LOCATION_MARKUP_METAPATH, item).forEach(child -> handleMarkup(child)); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); + item.getModelItemsByName("link").forEach(child -> handleLink(ObjectUtils.notNull(child))); + LOCATION_MARKUP_METAPATH.evaluate(item).asList() + .forEach(child -> handleMarkup(ObjectUtils.notNull((IRequiredValueModelNodeItem) child))); } } @Override - public void visitResource(@NotNull IRequiredValueModelNodeItem item) { + public void visitResource(@NonNull IRequiredValueModelNodeItem item) { Resource resource = (Resource) item.getValue(); EntityItem entity = getIndex().getEntity(ItemType.RESOURCE, ObjectUtils.notNull(resource.getUuid())); if (!entity.isResolved()) { entity.markResolved(); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); item.getModelItemsByName("citation").forEach(child -> { - child.getModelItemsByName("text").forEach(citationChild -> handleMarkup(citationChild)); - child.getModelItemsByName("prop").forEach(citationChild -> handleProperty(citationChild)); - child.getModelItemsByName("link").forEach(citationChild -> handleLink(citationChild)); + if (child != null) { + child.getModelItemsByName("text").forEach(citationChild -> handleMarkup(ObjectUtils.notNull(citationChild))); + child.getModelItemsByName("prop") + .forEach(citationChild -> handleProperty(ObjectUtils.notNull(citationChild))); + child.getModelItemsByName("link").forEach(citationChild -> handleLink(ObjectUtils.notNull(citationChild))); + } }); - evaluateToList(RESOURCE_MARKUP_METAPATH, item).forEach(child -> handleMarkup(child)); + RESOURCE_MARKUP_METAPATH.evaluate(item).asList() + .forEach(child -> handleMarkup(ObjectUtils.notNull((IRequiredValueModelNodeItem) child))); } } @Override - public void visitParameter(@NotNull IRequiredValueModelNodeItem item) { + public void visitParameter(@NonNull IRequiredValueModelNodeItem item) { Parameter parameter = (Parameter) item.getValue(); EntityItem entity = getIndex().getEntity(ItemType.PARAMETER, ObjectUtils.notNull(parameter.getId())); if (!entity.isResolved()) { entity.markResolved(); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); - item.getModelItemsByName("link").forEach(child -> handleLink(child)); - evaluateToList(PARAM_MARKUP_METAPATH, item).forEach(child -> handleMarkup(child)); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); + item.getModelItemsByName("link").forEach(child -> handleLink(ObjectUtils.notNull(child))); + PARAM_MARKUP_METAPATH.evaluate(item).asList() + .forEach(child -> handleMarkup(ObjectUtils.notNull((IRequiredValueModelNodeItem) child))); } } - @NotNull - @SuppressWarnings("unchecked") - private List<@NotNull R> evaluateToList( - @NotNull MetapathExpression metapath, - @NotNull T item) { - return ((ISequence) metapath.evaluateAs(item, ResultType.SEQUENCE)).asList(); - } - @Override - public void visitGroup(@NotNull IRequiredValueModelNodeItem item) { + public void visitGroup(@NonNull IRequiredValueModelNodeItem item) { CatalogGroup group = (CatalogGroup) item.getValue(); String id = group.getId(); @@ -293,22 +295,22 @@ public void visitGroup(@NotNull IRequiredValueModelNodeItem item) { if (resolve && getIndex().isSelected(group)) { // process children - item.getModelItemsByName("title").forEach(child -> handleMarkup(child)); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); - item.getModelItemsByName("link").forEach(child -> handleLink(child)); + item.getModelItemsByName("title").forEach(child -> handleMarkup(ObjectUtils.notNull(child))); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); + item.getModelItemsByName("link").forEach(child -> handleLink(ObjectUtils.notNull(child))); visitParts(item); // only process these if the current group is selected, since the group will only be selected if a // child is selected - item.getModelItemsByName("group").forEach(child -> visitGroup(child)); - item.getModelItemsByName("control").forEach(child -> visitControl(child)); + item.getModelItemsByName("group").forEach(child -> visitGroup(ObjectUtils.notNull(child))); + item.getModelItemsByName("control").forEach(child -> visitControl(ObjectUtils.notNull(child))); // skip parameters for now. These will be processed by a separate pass. } } @Override - public void visitControl(@NotNull IRequiredValueModelNodeItem item) { + public void visitControl(@NonNull IRequiredValueModelNodeItem item) { Control control = (Control) item.getValue(); EntityItem entity = getIndex().getEntity(ItemType.CONTROL, ObjectUtils.notNull(control.getId())); @@ -316,29 +318,29 @@ public void visitControl(@NotNull IRequiredValueModelNodeItem item) { entity.markResolved(); if (getIndex().isSelected(control)) { // process non-control, non-param children - item.getModelItemsByName("title").forEach(child -> handleMarkup(child)); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); - item.getModelItemsByName("link").forEach(child -> handleLink(child)); + item.getModelItemsByName("title").forEach(child -> handleMarkup(ObjectUtils.notNull(child))); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); + item.getModelItemsByName("link").forEach(child -> handleLink(ObjectUtils.notNull(child))); visitParts(item); // skip parameters for now. These will be processed by a separate pass. } // Always process these, since we don't know if the child control is selected - item.getModelItemsByName("control").forEach(child -> visitControl(child)); + item.getModelItemsByName("control").forEach(child -> visitControl(ObjectUtils.notNull(child))); } } - protected void visitParts(@NotNull IRequiredValueModelNodeItem groupOrControlItem) { + protected void visitParts(@NonNull IRequiredValueModelNodeItem groupOrControlItem) { PART_METAPATH.evaluate(groupOrControlItem).asStream() - .map(item -> (@NotNull IRequiredValueModelNodeItem) item) + .map(item -> (IRequiredValueModelNodeItem) item) .forEachOrdered(partItem -> { - visitPart(partItem); + visitPart(ObjectUtils.notNull(partItem)); }); } @Override - public void visitPart(@NotNull IRequiredValueModelNodeItem item) { + public void visitPart(@NonNull IRequiredValueModelNodeItem item) { ControlPart part = (ControlPart) item.getValue(); String id = part.getId(); @@ -356,25 +358,21 @@ public void visitPart(@NotNull IRequiredValueModelNodeItem item) { } if (resolve) { - item.getModelItemsByName("title").forEach(child -> handleMarkup(child)); - item.getModelItemsByName("prop").forEach(child -> handleProperty(child)); - item.getModelItemsByName("link").forEach(child -> handleLink(child)); - item.getModelItemsByName("prose").forEach(child -> handleMarkup(child)); - item.getModelItemsByName("part").forEach(child -> visitParts(child)); + item.getModelItemsByName("title").forEach(child -> handleMarkup(ObjectUtils.notNull(child))); + item.getModelItemsByName("prop").forEach(child -> handleProperty(ObjectUtils.notNull(child))); + item.getModelItemsByName("link").forEach(child -> handleLink(ObjectUtils.notNull(child))); + item.getModelItemsByName("prose").forEach(child -> handleMarkup(ObjectUtils.notNull(child))); + item.getModelItemsByName("part").forEach(child -> visitParts(ObjectUtils.notNull(child))); } } - @NotNull - private void handleMarkup(@NotNull IRequiredValueModelNodeItem item) { + private void handleMarkup(@NonNull IRequiredValueModelNodeItem item) { IMarkupItem markupItem = (IMarkupItem) FnData.fnDataItem(item); IMarkupText markup = markupItem.getValue(); - if (markup != null) { - handleMarkup(markup); - } + handleMarkup(markup); } - @NotNull - private void handleMarkup(@NotNull IMarkupText text) { + private void handleMarkup(@NonNull IMarkupText text) { for (Node node : CollectionUtil.toIterable(text.getNodesAsStream().iterator())) { if (node instanceof InsertAnchorNode) { handleInsert((InsertAnchorNode) node); @@ -384,24 +382,21 @@ private void handleMarkup(@NotNull IMarkupText text) { } } - @NotNull - private void handleInsert(@NotNull InsertAnchorNode node) { + private void handleInsert(@NonNull InsertAnchorNode node) { boolean retval = INSERT_POLICY.handleReference(node, this); if (LOGGER.isWarnEnabled() && !retval) { LOGGER.atWarn().log("unsupported insert type '{}'", node.getType().toString()); } } - @NotNull - private void handleAnchor(@NotNull InlineLinkNode node) { + private void handleAnchor(@NonNull InlineLinkNode node) { boolean result = ANCHOR_POLICY.handleReference(node, this); if (LOGGER.isWarnEnabled() && !result) { LOGGER.atWarn().log("unsupported anchor with href '{}'", node.getUrl().toString()); } } - @NotNull - private void handleProperty(@NotNull IRequiredValueModelNodeItem item) { + private void handleProperty(@NonNull IRequiredValueModelNodeItem item) { Property property = (Property) item.getValue(); QName qname = property.getQName(); @@ -413,8 +408,7 @@ private void handleProperty(@NotNull IRequiredValueModelNodeItem item) { } } - @NotNull - private void handleLink(@NotNull IRequiredValueModelNodeItem item) { + private void handleLink(@NonNull IRequiredValueModelNodeItem item) { Link link = (Link) item.getValue(); IReferencePolicy policy = null; String rel = link.getRel(); @@ -428,12 +422,11 @@ private void handleLink(@NotNull IRequiredValueModelNodeItem item) { } } - @SuppressWarnings("null") - protected void incrementReferenceCount(@NotNull ItemType type, @NotNull UUID identifier) { - incrementReferenceCount(type, identifier.toString()); + protected void incrementReferenceCount(@NonNull ItemType type, @NonNull UUID identifier) { + incrementReferenceCount(type, ObjectUtils.notNull(identifier.toString())); } - protected void incrementReferenceCount(@NotNull ItemType type, @NotNull String identifier) { + protected void incrementReferenceCount(@NonNull ItemType type, @NonNull String identifier) { EntityItem item = getIndex().getEntity(type, identifier); if (item == null) { if (LOGGER.isErrorEnabled()) { diff --git a/src/main/metaschema-bindings/oscal-metaschema-bindings.xml b/src/main/metaschema-bindings/oscal-metaschema-bindings.xml index f906df99..49e3dc2c 100644 --- a/src/main/metaschema-bindings/oscal-metaschema-bindings.xml +++ b/src/main/metaschema-bindings/oscal-metaschema-bindings.xml @@ -30,6 +30,14 @@ + + + + MappingEntry + + + diff --git a/src/main/resources/META-INF/services/gov.nist.secauto.metaschema.model.common.metapath.function.IFunctionLibrary b/src/main/resources/META-INF/services/gov.nist.secauto.metaschema.model.common.metapath.function.IFunctionLibrary deleted file mode 100644 index 9b1f3c21..00000000 --- a/src/main/resources/META-INF/services/gov.nist.secauto.metaschema.model.common.metapath.function.IFunctionLibrary +++ /dev/null @@ -1 +0,0 @@ -gov.nist.secauto.oscal.lib.metapath.function.library.OscalFunctionLibrary diff --git a/src/test/java/gov/nist/secauto/oscal/java/ExamplesTest.java b/src/test/java/gov/nist/secauto/oscal/java/ExamplesTest.java index 9d5342cf..c064ca23 100644 --- a/src/test/java/gov/nist/secauto/oscal/java/ExamplesTest.java +++ b/src/test/java/gov/nist/secauto/oscal/java/ExamplesTest.java @@ -26,15 +26,28 @@ package gov.nist.secauto.oscal.java; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import gov.nist.secauto.metaschema.binding.io.DeserializationFeature; import gov.nist.secauto.metaschema.binding.io.Format; import gov.nist.secauto.metaschema.binding.io.IBoundLoader; import gov.nist.secauto.metaschema.binding.io.ISerializer; +import gov.nist.secauto.metaschema.model.common.constraint.DefaultConstraintValidator; +import gov.nist.secauto.metaschema.model.common.constraint.FindingCollectingConstraintValidationHandler; +import gov.nist.secauto.metaschema.model.common.metapath.DynamicContext; +import gov.nist.secauto.metaschema.model.common.metapath.StaticContext; +import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; import gov.nist.secauto.oscal.lib.OscalBindingContext; import gov.nist.secauto.oscal.lib.model.Catalog; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolutionException; +import gov.nist.secauto.oscal.lib.profile.resolver.ProfileResolver; import org.junit.jupiter.api.Test; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -60,4 +73,32 @@ void simpleLoadAndSave() throws IOException { // serialize the catalog as yaml serializer.serialize(catalog, outDir.resolve("test-catalog.yaml")); } + + @Test + void testConstraintValidation() throws MalformedURLException, IOException, URISyntaxException, ProfileResolutionException { + // Initialize the Metaschema framework + OscalBindingContext bindingContext = OscalBindingContext.instance(); // manages the Metaschema model + IBoundLoader loader = bindingContext.newBoundLoader(); // supports loading OSCAL documents + loader.disableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS); + + IDocumentNodeItem nodeItem = loader.loadAsNodeItem(new URL( + "https://raw.githubusercontent.com/Rene2mt/fedramp-automation/a692b9385d8fbcacbb1d3e3d0b0d7e3c45a205d0/src/content/baselines/rev5/xml/FedRAMP_rev5_HIGH-baseline_profile.xml")); + + DynamicContext dynamicContext = new StaticContext().newDynamicContext(); + dynamicContext.setDocumentLoader(loader); + FindingCollectingConstraintValidationHandler handler = new FindingCollectingConstraintValidationHandler(); + DefaultConstraintValidator validator = new DefaultConstraintValidator(dynamicContext, handler); + + validator.validate(nodeItem); + validator.finalizeValidation(); + + assertTrue(handler.isPassing()); + + IDocumentNodeItem resolvedCatalog = new ProfileResolver().resolve(nodeItem); + + // Create a serializer which can be used to write multiple catalogs + ISerializer serializer = bindingContext.newSerializer(Format.YAML, Catalog.class); + // serialize the catalog as yaml + serializer.serialize((Catalog) resolvedCatalog.getValue(), System.out); + } } diff --git a/src/test/java/gov/nist/secauto/oscal/java/MetaschemaVisitorTest.java b/src/test/java/gov/nist/secauto/oscal/java/MetaschemaVisitorTest.java index 580715b5..c723521c 100644 --- a/src/test/java/gov/nist/secauto/oscal/java/MetaschemaVisitorTest.java +++ b/src/test/java/gov/nist/secauto/oscal/java/MetaschemaVisitorTest.java @@ -37,7 +37,7 @@ import gov.nist.secauto.oscal.lib.OscalBindingContext; import gov.nist.secauto.oscal.lib.metapath.function.library.ResolveProfile; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.junit.jupiter.api.Test; import java.io.File; @@ -57,7 +57,7 @@ void test() throws FileNotFoundException, IOException, BindingException, URISynt StaticContext staticContext = new StaticContext(); @SuppressWarnings("null") - @NotNull + @NonNull URI baseUri = new File("").getAbsoluteFile().toURI(); staticContext.setBaseUri(baseUri); DynamicContext dynamicContext = staticContext.newDynamicContext(); @@ -70,7 +70,7 @@ void test() throws FileNotFoundException, IOException, BindingException, URISynt IDocumentNodeItem nodeItem = loader.loadAsNodeItem(new URL( "https://raw.githubusercontent.com/usnistgov/oscal-content/master/nist.gov/SP800-53/rev5/xml/NIST_SP-800-53_rev5_HIGH-baseline_profile.xml")); - // @NotNull + // @NonNull // Profile profile = nodeItem.toBoundObject(); IDocumentNodeItem resolvedProfile = ResolveProfile.resolveProfile(nodeItem, dynamicContext); @@ -110,8 +110,8 @@ void test() throws FileNotFoundException, IOException, BindingException, URISynt } @SuppressWarnings("PMD") - private void evaluatePath(@NotNull MetapathExpression path, @NotNull INodeContext nodeContext, - @NotNull DynamicContext dynamicContext) { + private void evaluatePath(@NonNull MetapathExpression path, @NonNull INodeContext nodeContext, + @NonNull DynamicContext dynamicContext) { System.out.println("Path: " + path.getPath()); System.out.println("Compiled Path: " + path.toString()); diff --git a/src/test/java/gov/nist/secauto/oscal/java/OscalBindingContextTest.java b/src/test/java/gov/nist/secauto/oscal/java/OscalBindingContextTest.java index 7ab73759..d97eb1ba 100644 --- a/src/test/java/gov/nist/secauto/oscal/java/OscalBindingContextTest.java +++ b/src/test/java/gov/nist/secauto/oscal/java/OscalBindingContextTest.java @@ -38,7 +38,7 @@ import gov.nist.secauto.oscal.lib.model.Profile; import gov.nist.secauto.oscal.lib.model.SystemSecurityPlan; -import org.jetbrains.annotations.NotNull; +import edu.umd.cs.findbugs.annotations.NonNull; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -136,7 +136,7 @@ void testLoadProfileJson(@TempDir Path tempDir) throws BindingException, IOExcep // out.delete(); } - static Path newPath(@NotNull Path dir, @NotNull String filename) { + static Path newPath(@NonNull Path dir, @NonNull String filename) { return dir.resolve(filename); // return Path.of("target",filename); } @@ -159,7 +159,11 @@ void testCatalogXmlListItems(@TempDir Path tempDir) throws BindingException, IOE // File out = new File(tempDir.toFile(), "out.xml"); Path out = Paths.get("target/generated-test-resources/catalog-with-lists.xml"); - Files.createDirectories(out.getParent()); + + Path parent = out.getParent(); + if (parent != null) { + Files.createDirectories(parent); + } IBindingContext context = IBindingContext.instance(); ISerializer serializer = context.newSerializer(Format.XML, Catalog.class); diff --git a/src/test/java/gov/nist/secauto/oscal/java/ReadWriteTest.java b/src/test/java/gov/nist/secauto/oscal/java/ReadWriteTest.java index 4a35dc29..3348f637 100644 --- a/src/test/java/gov/nist/secauto/oscal/java/ReadWriteTest.java +++ b/src/test/java/gov/nist/secauto/oscal/java/ReadWriteTest.java @@ -30,6 +30,7 @@ import gov.nist.secauto.metaschema.binding.IBindingContext; import gov.nist.secauto.metaschema.binding.io.BindingException; +import gov.nist.secauto.metaschema.binding.io.DeserializationFeature; import gov.nist.secauto.metaschema.binding.io.Format; import gov.nist.secauto.metaschema.binding.io.IDeserializer; import gov.nist.secauto.metaschema.binding.io.ISerializer; @@ -99,6 +100,7 @@ private static void chainReadWrite(File xmlSource, Class clazz, P // XML { IDeserializer deserializer = context.newDeserializer(Format.XML, clazz); + deserializer.disableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS); obj = measureDeserializer("XML", xmlSource, deserializer, iterations); File out = new File(tempDir.toFile(), "out.xml"); @@ -113,6 +115,7 @@ private static void chainReadWrite(File xmlSource, Class clazz, P measureSerializer(obj, "JSON", out, serializer, iterations); IDeserializer deserializer = context.newDeserializer(Format.JSON, clazz); + deserializer.disableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS); obj = measureDeserializer("JSON", out, deserializer, iterations); } @@ -123,6 +126,7 @@ private static void chainReadWrite(File xmlSource, Class clazz, P measureSerializer(obj, "YAML", out, serializer, iterations); IDeserializer deserializer = context.newDeserializer(Format.YAML, clazz); + deserializer.disableFeature(DeserializationFeature.DESERIALIZE_VALIDATE_CONSTRAINTS); measureDeserializer("YAML", out, deserializer, iterations); } } @@ -140,7 +144,6 @@ void testOscalCatalogMetrics(@TempDir Path tempDir) throws IOException, BindingE // outDir.mkdirs(); // Path outPath = outDir.toPath(); Path outPath = tempDir; - // chainReadWrite(catalogSourceXml, Catalog.class, tempDir, 50); chainReadWrite(catalogSourceXml, Catalog.class, outPath, 1); } } diff --git a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilterTest.java b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilterTest.java index 17d654a7..68247445 100644 --- a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilterTest.java +++ b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/DefaultControlSelectionFilterTest.java @@ -32,8 +32,8 @@ import gov.nist.secauto.oscal.lib.model.ProfileSelectControlById; import gov.nist.secauto.oscal.lib.model.control.catalog.IControl; import gov.nist.secauto.oscal.lib.model.control.profile.IProfileSelectControlById; + import org.apache.commons.lang3.tuple.Pair; -import org.jetbrains.annotations.NotNull; import org.jmock.Expectations; import org.jmock.auto.Mock; import org.jmock.imposters.ByteBuddyClassImposteriser; @@ -44,6 +44,8 @@ import java.util.Collections; import java.util.List; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + class DefaultControlSelectionFilterTest { @RegisterExtension final JUnit5Mockery context = new JUnit5Mockery() { @@ -53,15 +55,15 @@ class DefaultControlSelectionFilterTest { }; @Mock - private IProfileSelectControlById selectControlByIdA; + private IProfileSelectControlById selectControlByIdA; // NOPMD - injected @Mock - private ProfileSelectControlById.Matching matchingA; + private ProfileSelectControlById.Matching matchingA; // NOPMD - injected @Mock - private ProfileSelectControlById.Matching matchingB; + private ProfileSelectControlById.Matching matchingB; // NOPMD - injected @Mock - private IProfileSelectControlById selectControlByIdB; + private IProfileSelectControlById selectControlByIdB; // NOPMD - injected - @SuppressWarnings("null") + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") private IControlSelectionFilter newEmptyFilter() { context.checking(new Expectations() { { // NOPMD - intentional @@ -77,7 +79,7 @@ private IControlSelectionFilter newEmptyFilter() { return new DefaultControlSelectionFilter(List.of(selectControlByIdA)); } - @SuppressWarnings("null") + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") private IControlSelectionFilter newSingleSelectionFilter() { final List withIds = List.of("test1", "test2"); context.checking(new Expectations() { @@ -96,7 +98,7 @@ private IControlSelectionFilter newSingleSelectionFilter() { return new DefaultControlSelectionFilter(List.of(selectControlByIdA)); } - @SuppressWarnings("null") + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") private IControlSelectionFilter newSingleSelectionWithChildFilter() { final List withIds = List.of("test1", "test2"); context.checking(new Expectations() { @@ -115,7 +117,7 @@ private IControlSelectionFilter newSingleSelectionWithChildFilter() { return new DefaultControlSelectionFilter(List.of(selectControlByIdA)); } - @SuppressWarnings("null") + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") private IControlSelectionFilter newMultipleSelectionFilter() { final List withIdsA = List.of("test1", "test2", "example1"); final List withIdsB = List.of("test3", "test4"); @@ -148,6 +150,7 @@ private IControlSelectionFilter newMultipleSelectionFilter() { * Test the filtering of an empty set of match criteria */ @Test + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") void testEmpty() { @SuppressWarnings("null") final IControl control1 = context.mock(IControl.class); @@ -159,7 +162,7 @@ void testEmpty() { }); IControlSelectionFilter filter = newEmptyFilter(); - Pair<@NotNull Boolean, @NotNull Boolean> pair = filter.apply(control1); + Pair pair = filter.apply(control1); assertFalse(pair.getLeft()); assertFalse(pair.getRight()); } @@ -168,6 +171,7 @@ void testEmpty() { * Test the filtering of a single match criteria using "with-ids". */ @Test + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") void testSingleSelectionWithIdsFilter() { @SuppressWarnings("null") final IControl control1 = context.mock(IControl.class, "control1"); @@ -183,7 +187,7 @@ void testSingleSelectionWithIdsFilter() { }); IControlSelectionFilter filter = newSingleSelectionFilter(); - Pair<@NotNull Boolean, @NotNull Boolean> pair = filter.apply(control1); + Pair pair = filter.apply(control1); assertFalse(pair.getLeft()); assertFalse(pair.getRight()); @@ -196,6 +200,7 @@ void testSingleSelectionWithIdsFilter() { * Test the filtering of a single match criteria using "with-ids" and "with-child=yes". */ @Test + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") void testSingleSelectionWithIdsWithChildFilter() { @SuppressWarnings("null") final IControl control1 = context.mock(IControl.class, "control1"); @@ -211,7 +216,7 @@ void testSingleSelectionWithIdsWithChildFilter() { }); IControlSelectionFilter filter = newSingleSelectionWithChildFilter(); - Pair<@NotNull Boolean, @NotNull Boolean> pair = filter.apply(control1); + Pair pair = filter.apply(control1); assertFalse(pair.getLeft()); assertFalse(pair.getRight()); @@ -224,6 +229,7 @@ void testSingleSelectionWithIdsWithChildFilter() { * Test the filtering of multiple match criteria. */ @Test + @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT") void testMultipleSelectionFilter() { @SuppressWarnings("null") final IControl control1 = context.mock(IControl.class, "control1"); @@ -259,7 +265,7 @@ void testMultipleSelectionFilter() { }); IControlSelectionFilter filter = newMultipleSelectionFilter(); - Pair<@NotNull Boolean, @NotNull Boolean> pair = filter.apply(control1); + Pair pair = filter.apply(control1); assertFalse(pair.getLeft()); assertFalse(pair.getRight()); diff --git a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportTest.java b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportTest.java index 096fec37..4e520b00 100644 --- a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportTest.java +++ b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ImportTest.java @@ -26,7 +26,6 @@ package gov.nist.secauto.oscal.lib.profile.resolver; -import gov.nist.secauto.metaschema.binding.io.BindingException; import gov.nist.secauto.metaschema.binding.model.IAssemblyClassBinding; import gov.nist.secauto.metaschema.model.common.IRootAssemblyDefinition; import gov.nist.secauto.metaschema.model.common.metapath.item.DefaultNodeItemFactory; @@ -36,44 +35,46 @@ import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; import gov.nist.secauto.oscal.lib.OscalBindingContext; import gov.nist.secauto.oscal.lib.model.Catalog; -import gov.nist.secauto.oscal.lib.model.Control; import gov.nist.secauto.oscal.lib.model.IncludeAll; import gov.nist.secauto.oscal.lib.model.Profile; import gov.nist.secauto.oscal.lib.model.ProfileImport; -import gov.nist.secauto.oscal.lib.model.ProfileSelectControlById; +import gov.nist.secauto.oscal.lib.model.control.catalog.AbstractControl; +import gov.nist.secauto.oscal.lib.model.control.profile.AbstractProfileSelectControlById; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import java.net.URI; import java.nio.file.Paths; import java.util.Collections; +import edu.umd.cs.findbugs.annotations.NonNull; + class ImportTest { - @NotNull - private IDocumentNodeItem newImportedCatalog() { + @NonNull + private static IDocumentNodeItem newImportedCatalog() { // setup the imported catalog Catalog importedCatalog = new Catalog(); - importedCatalog.addControl(Control.builder("control1") + importedCatalog.addControl(AbstractControl.builder("control1") .title("Control 1") .build()); - importedCatalog.addControl(Control.builder("control2") + importedCatalog.addControl(AbstractControl.builder("control2") .title("Control 2") .build()); return DefaultNodeItemFactory.instance().newDocumentNodeItem( IRootAssemblyDefinition.toRootAssemblyDefinition( - (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class)), + ObjectUtils.notNull( + (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class))), importedCatalog, ObjectUtils.notNull(Paths.get("").toUri())); } @SuppressWarnings("null") @Test - void test() throws BindingException { + void test() throws ProfileResolutionException { URI cwd = Paths.get("").toUri(); // setup the imported catalog @@ -85,7 +86,7 @@ void test() throws BindingException { ProfileImport profileImport = new ProfileImport(); profileImport.setIncludeAll(new IncludeAll()); profileImport.setExcludeControls(Collections.singletonList( - ProfileSelectControlById.builder() + AbstractProfileSelectControlById.builder() .withId("control1") .build())); profileImport.setHref(cwd); @@ -93,7 +94,8 @@ void test() throws BindingException { IDocumentNodeItem profileDocumentItem = DefaultNodeItemFactory.instance().newDocumentNodeItem( IRootAssemblyDefinition.toRootAssemblyDefinition( - (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Profile.class)), + ObjectUtils.notNull( + (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Profile.class))), profile, cwd); @@ -104,7 +106,7 @@ void test() throws BindingException { profileDocumentItem.getModelItemsByName("profile").stream() .flatMap(root -> root.getModelItemsByName("import").stream()))) { - Import catalogImport = new Import(profileDocumentItem, importItem); + Import catalogImport = new Import(profileDocumentItem, importItem); // NOPMD - intentional catalogImport.resolve(importedCatalogDocumentItem, resolvedCatalog); } } diff --git a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ModifyPhaseUtilsTest.java b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ModifyPhaseUtilsTest.java new file mode 100644 index 00000000..e54b4089 --- /dev/null +++ b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ModifyPhaseUtilsTest.java @@ -0,0 +1,101 @@ +/* + * Portions of this software was developed by employees of the National Institute + * of Standards and Technology (NIST), an agency of the Federal Government and is + * being made available as a public service. Pursuant to title 17 United States + * Code Section 105, works of NIST employees are not subject to copyright + * protection in the United States. This software may be subject to foreign + * copyright. Permission in the United States and in foreign countries, to the + * extent that NIST may hold copyright, to use, copy, modify, create derivative + * works, and distribute this software and its documentation without fee is hereby + * granted on a non-exclusive basis, provided that this notice and disclaimer + * of warranty appears in all copies. + * + * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER + * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY + * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM + * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE + * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT + * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT, + * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM, + * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY, + * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR + * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT + * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER. + */ + +package gov.nist.secauto.oscal.lib.profile.resolver; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.stream.Collectors; + +import edu.umd.cs.findbugs.annotations.NonNull; +import edu.umd.cs.findbugs.annotations.Nullable; + +class ModifyPhaseUtilsTest { + + @Test + void testMergeOrdering() { + List originalItems = List.of( + item("A"), + item("id1", "B"), + item("C")); + + List newItems = List.of( + item("D"), + item("id1", "E"), + item("F")); + + List result + = ModifyPhaseUtils.merge(originalItems, newItems, ModifyPhaseUtils.identifierKey(TestItem::getIdentifier)); + + assertEquals( + List.of("A", "C", "D", "E", "F"), + result.stream() + .map(item -> item.getValue()) + .collect(Collectors.toList())); + } + + private TestItem item(@NonNull String value) { + return item(null, value); + } + + private TestItem item(@Nullable String identifier, @NonNull String value) { + return new TestItem(identifier, value); + } + + private static class TestItem { + @Nullable + private final String identifier; + @NonNull + private final String value; + + private TestItem(@Nullable String identifier, @NonNull String value) { + this.identifier = identifier; + this.value = value; + } + + public String getIdentifier() { + return identifier; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return new StringBuffer() + .append('[') + .append(getIdentifier()) + .append(',') + .append(getValue()) + .append(']') + .toString(); + } + } +} diff --git a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionTests.java b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionTests.java index 08fdfa7f..14a2338d 100644 --- a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionTests.java +++ b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/ProfileResolutionTests.java @@ -30,7 +30,6 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import gov.nist.secauto.metaschema.binding.io.BindingException; import gov.nist.secauto.metaschema.binding.io.DefaultBoundLoader; import gov.nist.secauto.metaschema.binding.io.Format; import gov.nist.secauto.metaschema.binding.io.ISerializer; @@ -48,7 +47,6 @@ import org.assertj.core.api.Assertions; import org.hamcrest.CoreMatchers; import org.hamcrest.MatcherAssert; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -67,6 +65,8 @@ import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; +import edu.umd.cs.findbugs.annotations.NonNull; + class ProfileResolutionTests { private static final String XSLT_PATH = "oscal/src/utils/util/resolver-pipeline/oscal-profile-test-helper.xsl"; private static final String PROFILE_UNIT_TEST_PATH @@ -102,17 +102,17 @@ public static ProfileResolver getProfileResolver() { return profileResolver; } - private Catalog resolveProfile(@NotNull Path profileFile) - throws FileNotFoundException, BindingException, IOException { + private static Catalog resolveProfile(@NonNull Path profileFile) + throws FileNotFoundException, IOException, ProfileResolutionException { return (Catalog) getProfileResolver().resolveProfile(profileFile).getValue(); } - private Catalog resolveProfile(@NotNull File profileFile) - throws FileNotFoundException, BindingException, IOException { + private static Catalog resolveProfile(@NonNull File profileFile) + throws FileNotFoundException, IOException, ProfileResolutionException { return (Catalog) getProfileResolver().resolveProfile(profileFile).getValue(); } - private String transformXml(Source source) throws SaxonApiException { + private static String transformXml(Source source) throws SaxonApiException { net.sf.saxon.s9api.Serializer out = getProcessor().newSerializer(); out.setOutputProperty(net.sf.saxon.s9api.Serializer.Property.METHOD, "xml"); // out.setOutputProperty(net.sf.saxon.s9api.Serializer.Property.INDENT, "yes"); @@ -128,16 +128,16 @@ private String transformXml(Source source) throws SaxonApiException { @ParameterizedTest @CsvFileSource(resources = "/profile-tests.csv", numLinesToSkip = 1) - void test(String profileName) throws IllegalStateException, IOException, BindingException, SaxonApiException { + void test(String profileName) throws IOException, SaxonApiException, ProfileResolutionException { performTest(profileName); } @Test - void testSingle() throws IllegalStateException, IOException, BindingException, SaxonApiException { - performTest("merge-keep-resources"); + void testSingle() throws IOException, SaxonApiException, ProfileResolutionException { + performTest("modify-adds"); } - void performTest(String profileName) throws IllegalStateException, IOException, BindingException, SaxonApiException { + void performTest(String profileName) throws IOException, SaxonApiException, ProfileResolutionException { String profileLocation = String.format("%s/%s_profile.xml", PROFILE_UNIT_TEST_PATH, profileName); File profileFile = new File(profileLocation); @@ -152,7 +152,7 @@ void performTest(String profileName) throws IllegalStateException, IOException, Assertions.assertThat(catalog.getMetadata().getProps()).filteredOn("name", "resolution-tool").extracting("value") .hasSize(1); - ISerializer<@NotNull Catalog> serializer = OscalBindingContext.instance().newSerializer(Format.XML, Catalog.class); + ISerializer serializer = OscalBindingContext.instance().newSerializer(Format.XML, Catalog.class); StringWriter writer = new StringWriter(); serializer.serialize(catalog, writer); @@ -170,7 +170,7 @@ void performTest(String profileName) throws IllegalStateException, IOException, } @Test - void testBrokenLink() throws IllegalStateException, IOException, BindingException { + void testBrokenLink() { String profileLocation = String.format("%s/broken_profile.xml", PROFILE_UNIT_TEST_PATH); File profileFile = new File(profileLocation); @@ -181,12 +181,11 @@ void testBrokenLink() throws IllegalStateException, IOException, BindingExceptio } @Test - void testCircularLink() throws IllegalStateException, IOException, BindingException { + void testCircularLink() { String profileLocation = String.format("%s/circular_profile.xml", PROFILE_UNIT_TEST_PATH); File profileFile = new File(profileLocation); - @SuppressWarnings("null") IOException exceptionThrown = assertThrows(IOException.class, () -> { resolveProfile(profileFile); }); @@ -195,7 +194,7 @@ void testCircularLink() throws IllegalStateException, IOException, BindingExcept } @Test - void testOscalVersion() throws IllegalStateException, IOException, BindingException { + void testOscalVersion() throws IOException, ProfileResolutionException { Path profileFile = Paths.get(JUNIT_TEST_PATH, "content/test-oscal-version-profile.xml"); Catalog catalog = resolveProfile(profileFile); assertNotNull(catalog); @@ -203,7 +202,7 @@ void testOscalVersion() throws IllegalStateException, IOException, BindingExcept } @Test - void testImportResourceRelativeLink() throws IOException, BindingException { + void testImportResourceRelativeLink() throws IOException, ProfileResolutionException { Path profilePath = Paths.get(JUNIT_TEST_PATH, "content/profile-relative-links-resource.xml"); Catalog resolvedCatalog = resolveProfile(profilePath); assertNotNull(resolvedCatalog); diff --git a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/TestUtil.java b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/TestUtil.java index 1d0d2d18..1cdd4342 100644 --- a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/TestUtil.java +++ b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/TestUtil.java @@ -33,43 +33,45 @@ import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; import gov.nist.secauto.oscal.lib.OscalBindingContext; import gov.nist.secauto.oscal.lib.model.Catalog; -import gov.nist.secauto.oscal.lib.model.CatalogGroup; -import gov.nist.secauto.oscal.lib.model.Control; -import gov.nist.secauto.oscal.lib.model.ControlPart; -import gov.nist.secauto.oscal.lib.model.Parameter; -import gov.nist.secauto.oscal.lib.model.Property; -import org.jetbrains.annotations.NotNull; +import gov.nist.secauto.oscal.lib.model.control.AbstractParameter; +import gov.nist.secauto.oscal.lib.model.control.AbstractPart; +import gov.nist.secauto.oscal.lib.model.control.catalog.AbstractCatalogGroup; +import gov.nist.secauto.oscal.lib.model.control.catalog.AbstractControl; +import gov.nist.secauto.oscal.lib.model.metadata.AbstractProperty; +import gov.nist.secauto.oscal.lib.model.metadata.IProperty; import java.nio.file.Paths; import java.util.UUID; +import edu.umd.cs.findbugs.annotations.NonNull; + public final class TestUtil { - @NotNull + @NonNull public static final IIdentifierMapper UUID_CONCAT_ID_MAPPER = new IIdentifierMapper() { @Override - public String mapRoleIdentifier(@NotNull String identifier) { + public String mapRoleIdentifier(@NonNull String identifier) { return identifier + "-" + UUID.randomUUID().toString(); } @Override - public String mapControlIdentifier(@NotNull String identifier) { + public String mapControlIdentifier(@NonNull String identifier) { return identifier + "-" + UUID.randomUUID().toString(); } @Override - public String mapGroupIdentifier(@NotNull String identifier) { + public String mapGroupIdentifier(@NonNull String identifier) { return identifier + "-" + UUID.randomUUID().toString(); } @Override - public String mapParameterIdentifier(@NotNull String identifier) { + public String mapParameterIdentifier(@NonNull String identifier) { return identifier + "-" + UUID.randomUUID().toString(); } @Override - public @NotNull String mapPartIdentifier(@NotNull String identifier) { + public @NonNull String mapPartIdentifier(@NonNull String identifier) { return identifier + "-" + UUID.randomUUID().toString(); } }; @@ -78,86 +80,86 @@ private TestUtil() { // disable construction } - @NotNull + @NonNull public static IDocumentNodeItem newImportedCatalog() { // setup the imported catalog Catalog importedCatalog = new Catalog(); importedCatalog.setUuid(UUID.randomUUID()); - importedCatalog.addParam(Parameter.builder("param1") + importedCatalog.addParam(AbstractParameter.builder("param1") .build()); - importedCatalog.addGroup(CatalogGroup.builder("group1") + importedCatalog.addGroup(AbstractCatalogGroup.builder("group1") .title("Group 1") - .part(ControlPart.builder("statement") // NOPMD - no need to reduce literals + .part(AbstractPart.builder("statement") // NOPMD - no need to reduce literals .prose("group 1 part 1") .build()) - .param(Parameter.builder("param2") + .param(AbstractParameter.builder("param2") .build()) - .control(Control.builder("control1") + .control(AbstractControl.builder("control1") .title("Control 1") - .param(Parameter.builder("param3") + .param(AbstractParameter.builder("param3") .build()) - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("A {{ insert: param, param1}} reference.") .build()) - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("group 1 control 1 part 1") - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("group 1 control 1 part 1.a") .build()) - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("group 1 control 1 part 1.b") .build()) .build()) - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("group 1 control 1 part 2") .build()) .build()) // to be filtered - .control(Control.builder("control2") + .control(AbstractControl.builder("control2") .title("Control 2") - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("A {{ insert: param, param2}} reference.") .build()) .build()) .build()); - importedCatalog.addGroup(CatalogGroup.builder("group2") + importedCatalog.addGroup(AbstractCatalogGroup.builder("group2") .title("Group 2") - .param(Parameter.builder("param4") - .prop(Property.builder("aggregates") - .namespace(Property.RMF_NAMESPACE) + .param(AbstractParameter.builder("param4") + .prop(AbstractProperty.builder("aggregates") + .namespace(IProperty.RMF_NAMESPACE) .value("param2") .build()) .build()) - .control(Control.builder("control3") + .control(AbstractControl.builder("control3") .title("Control 3") .build()) - .control(Control.builder("control4") + .control(AbstractControl.builder("control4") .title("Control 4") .build()) - .group(CatalogGroup.builder("group3") + .group(AbstractCatalogGroup.builder("group3") .title("Group 3") // to be filtered - .control(Control.builder("control5") + .control(AbstractControl.builder("control5") .title("Control 5") .build()) .build()) - .control(Control.builder("control6") + .control(AbstractControl.builder("control6") .title("Control 6") - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("A {{ insert: param, param4}} reference.") .build()) .build()) // to be filtered - .control(Control.builder("control7") + .control(AbstractControl.builder("control7") .title("Control 7") - .param(Parameter.builder("param5") + .param(AbstractParameter.builder("param5") .build()) - .control(Control.builder("control8") + .control(AbstractControl.builder("control8") .title("Control 8") - .part(ControlPart.builder("statement") + .part(AbstractPart.builder("statement") .prose("A {{ insert: param, param5}} reference.") .build()) .build()) @@ -166,7 +168,8 @@ public static IDocumentNodeItem newImportedCatalog() { return DefaultNodeItemFactory.instance().newDocumentNodeItem( IRootAssemblyDefinition.toRootAssemblyDefinition( - (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class)), + ObjectUtils.notNull( + (IAssemblyClassBinding) OscalBindingContext.instance().getClassBinding(Catalog.class))), importedCatalog, ObjectUtils.notNull(Paths.get("").toUri())); } diff --git a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitorTest.java b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitorTest.java index 7fbc51cc..cd54607c 100644 --- a/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitorTest.java +++ b/src/test/java/gov/nist/secauto/oscal/lib/profile/resolver/policy/ReferenceCountingVisitorTest.java @@ -28,6 +28,7 @@ import gov.nist.secauto.metaschema.binding.io.Format; import gov.nist.secauto.metaschema.model.common.metapath.item.IDocumentNodeItem; +import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; import gov.nist.secauto.oscal.lib.OscalBindingContext; import gov.nist.secauto.oscal.lib.model.Catalog; import gov.nist.secauto.oscal.lib.profile.resolver.ControlSelectionVisitor; @@ -42,6 +43,7 @@ class ReferenceCountingVisitorTest { + @SuppressWarnings("null") @Test void test() throws IOException { // setup the imported catalog @@ -65,7 +67,7 @@ void test() throws IOException { OscalBindingContext.instance() .newSerializer(Format.YAML, Catalog.class) - .serialize((Catalog) importedCatalogDocumentItem.getValue(), System.out); + .serialize(ObjectUtils.requireNonNull((Catalog) importedCatalogDocumentItem.getValue()), System.out); } } diff --git a/src/test/resources/profile-tests.csv b/src/test/resources/profile-tests.csv index dcb139a1..454b686e 100644 --- a/src/test/resources/profile-tests.csv +++ b/src/test/resources/profile-tests.csv @@ -11,4 +11,4 @@ include-loose-param-test include-match-test merge-implicit-keep merge-keep-resources -#modify-adds +modify-adds