1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package gov.nist.secauto.metaschema.cli.commands;
7   
8   import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext;
9   import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor;
10  import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration;
11  import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
12  import gov.nist.secauto.metaschema.core.model.IModule;
13  import gov.nist.secauto.metaschema.core.model.MetaschemaException;
14  import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
15  import gov.nist.secauto.metaschema.core.model.util.JsonUtil;
16  import gov.nist.secauto.metaschema.core.model.util.XmlUtil;
17  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
18  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
19  import gov.nist.secauto.metaschema.databind.DefaultBindingContext;
20  import gov.nist.secauto.metaschema.databind.IBindingContext;
21  import gov.nist.secauto.metaschema.schemagen.ISchemaGenerator;
22  import gov.nist.secauto.metaschema.schemagen.ISchemaGenerator.SchemaFormat;
23  import gov.nist.secauto.metaschema.schemagen.SchemaGenerationFeature;
24  
25  import org.apache.commons.cli.CommandLine;
26  import org.apache.commons.cli.Option;
27  import org.json.JSONObject;
28  
29  import java.io.BufferedReader;
30  import java.io.IOException;
31  import java.net.URI;
32  import java.net.URISyntaxException;
33  import java.net.URL;
34  import java.nio.charset.StandardCharsets;
35  import java.nio.file.Files;
36  import java.nio.file.Path;
37  import java.nio.file.Paths;
38  import java.util.ArrayList;
39  import java.util.Collection;
40  import java.util.List;
41  import java.util.Set;
42  
43  import javax.xml.transform.Source;
44  
45  import edu.umd.cs.findbugs.annotations.NonNull;
46  
47  public class ValidateContentUsingModuleCommand
48      extends AbstractValidateContentCommand {
49    @NonNull
50    private static final String COMMAND = "validate-content";
51  
52    @Override
53    public String getName() {
54      return COMMAND;
55    }
56  
57    @Override
58    public String getDescription() {
59      return "Verify that the provided resource is well-formed and valid to the provided Module-based model.";
60    }
61  
62    @Override
63    public Collection<? extends Option> gatherOptions() {
64      Collection<? extends Option> orig = super.gatherOptions();
65  
66      List<Option> retval = new ArrayList<>(orig.size() + 1);
67      retval.addAll(orig);
68      retval.add(MetaschemaCommands.METASCHEMA_OPTION);
69  
70      return CollectionUtil.unmodifiableCollection(retval);
71    }
72  
73    @Override
74    public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) {
75      return new OscalCommandExecutor(callingContext, commandLine);
76    }
77  
78    private final class OscalCommandExecutor
79        extends AbstractValidationCommandExecutor {
80  
81      private Path tempDir;
82      private IModule module;
83  
84      private OscalCommandExecutor(
85          @NonNull CallingContext callingContext,
86          @NonNull CommandLine commandLine) {
87        super(callingContext, commandLine);
88      }
89  
90      @NonNull
91      private Path getTempDir() throws IOException {
92        if (tempDir == null) {
93          tempDir = Files.createTempDirectory("validation-");
94          tempDir.toFile().deleteOnExit();
95        }
96        assert tempDir != null;
97        return tempDir;
98      }
99  
100     @NonNull
101     private IModule getModule(@NonNull Set<IConstraintSet> constraintSets)
102         throws MetaschemaException, IOException {
103       URI cwd = ObjectUtils.notNull(Paths.get("").toAbsolutePath().toUri());
104 
105       if (module == null) {
106         try {
107           module = MetaschemaCommands.handleModule(getCommandLine(), cwd, constraintSets);
108         } catch (URISyntaxException ex) {
109           throw new IOException(String.format("Cannot load module as '%s' is not a valid file or URL.", ex.getInput()),
110               ex);
111         }
112       }
113 
114       assert module != null;
115       return module;
116     }
117 
118     @NonNull
119     private IModule getModule() {
120       // should be initialized already
121       return ObjectUtils.requireNonNull(module);
122     }
123 
124     @Override
125     protected IBindingContext getBindingContext(@NonNull Set<IConstraintSet> constraintSets)
126         throws MetaschemaException, IOException {
127 
128       IBindingContext retval = new DefaultBindingContext();
129       retval.registerModule(getModule(constraintSets), getTempDir());
130       return retval;
131     }
132 
133     @Override
134     public List<Source> getXmlSchemas(@NonNull URL targetResource) throws IOException {
135       Path schemaFile = Files.createTempFile(getTempDir(), "schema-", ".xml");
136       assert schemaFile != null;
137       IMutableConfiguration<SchemaGenerationFeature<?>> configuration = new DefaultConfiguration<>();
138       ISchemaGenerator.generateSchema(getModule(), schemaFile, SchemaFormat.XML, configuration);
139       return ObjectUtils.requireNonNull(List.of(
140           XmlUtil.getStreamSource(ObjectUtils.notNull(schemaFile.toUri().toURL()))));
141     }
142 
143     @Override
144     public JSONObject getJsonSchema(@NonNull JSONObject json) throws IOException {
145       Path schemaFile = Files.createTempFile(getTempDir(), "schema-", ".json");
146       assert schemaFile != null;
147       IMutableConfiguration<SchemaGenerationFeature<?>> configuration = new DefaultConfiguration<>();
148       ISchemaGenerator.generateSchema(getModule(), schemaFile, SchemaFormat.JSON, configuration);
149       try (BufferedReader reader = ObjectUtils.notNull(Files.newBufferedReader(schemaFile, StandardCharsets.UTF_8))) {
150         return JsonUtil.toJsonObject(reader);
151       }
152     }
153   }
154 
155 }