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.CommandExecutionException;
10  import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor;
11  import gov.nist.secauto.metaschema.core.configuration.DefaultConfiguration;
12  import gov.nist.secauto.metaschema.core.configuration.IMutableConfiguration;
13  import gov.nist.secauto.metaschema.core.model.IModule;
14  import gov.nist.secauto.metaschema.core.model.constraint.IConstraintSet;
15  import gov.nist.secauto.metaschema.core.model.validation.JsonSchemaContentValidator;
16  import gov.nist.secauto.metaschema.core.model.validation.XmlSchemaContentValidator;
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.IBindingContext;
20  import gov.nist.secauto.metaschema.databind.IBindingContext.ISchemaValidationProvider;
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  import org.json.JSONTokener;
29  import org.xml.sax.SAXException;
30  
31  import java.io.IOException;
32  import java.io.Reader;
33  import java.io.StringReader;
34  import java.io.StringWriter;
35  import java.net.URL;
36  import java.util.ArrayList;
37  import java.util.Collection;
38  import java.util.List;
39  import java.util.Set;
40  
41  import javax.xml.transform.stream.StreamSource;
42  
43  import edu.umd.cs.findbugs.annotations.NonNull;
44  
45  /**
46   * This command implementation supports validation of a content instance based
47   * on a provided Metaschema module.
48   */
49  class ValidateContentUsingModuleCommand
50      extends AbstractValidateContentCommand {
51    @NonNull
52    private static final String COMMAND = "validate-content";
53  
54    @Override
55    public String getName() {
56      return COMMAND;
57    }
58  
59    @Override
60    public String getDescription() {
61      return "Verify that the provided resource is well-formed and valid to the provided Module-based model.";
62    }
63  
64    @Override
65    public Collection<? extends Option> gatherOptions() {
66      Collection<? extends Option> orig = super.gatherOptions();
67  
68      List<Option> retval = new ArrayList<>(orig.size() + 1);
69      retval.addAll(orig);
70      retval.add(MetaschemaCommands.METASCHEMA_REQUIRED_OPTION);
71  
72      return CollectionUtil.unmodifiableCollection(retval);
73    }
74  
75    @Override
76    public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) {
77      return new CommandExecutor(callingContext, commandLine);
78    }
79  
80    private final class CommandExecutor
81        extends AbstractValidationCommandExecutor {
82  
83      private CommandExecutor(
84          @NonNull CallingContext callingContext,
85          @NonNull CommandLine commandLine) {
86        super(callingContext, commandLine);
87      }
88  
89      @Override
90      protected IBindingContext getBindingContext(@NonNull Set<IConstraintSet> constraintSets)
91          throws CommandExecutionException {
92        return MetaschemaCommands.newBindingContextWithDynamicCompilation(constraintSets);
93      }
94  
95      @Override
96      protected IModule getModule(
97          CommandLine commandLine,
98          IBindingContext bindingContext) throws CommandExecutionException {
99        return MetaschemaCommands.loadModule(
100           commandLine,
101           MetaschemaCommands.METASCHEMA_REQUIRED_OPTION,
102           ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()),
103           bindingContext);
104     }
105 
106     @Override
107     protected ISchemaValidationProvider getSchemaValidationProvider(
108         IModule module,
109         CommandLine commandLine,
110         IBindingContext bindingContext) {
111       return new ModuleValidationProvider(module);
112     }
113 
114   }
115 
116   private static final class ModuleValidationProvider implements ISchemaValidationProvider {
117     @NonNull
118     private final IModule module;
119 
120     public ModuleValidationProvider(@NonNull IModule module) {
121       this.module = module;
122     }
123 
124     @Override
125     public XmlSchemaContentValidator getXmlSchemas(
126         @NonNull URL targetResource,
127         @NonNull IBindingContext bindingContext) throws IOException, SAXException {
128       IMutableConfiguration<SchemaGenerationFeature<?>> configuration = new DefaultConfiguration<>();
129 
130       try (StringWriter writer = new StringWriter()) {
131         ISchemaGenerator.generateSchema(module, writer, SchemaFormat.XML, configuration);
132         try (Reader reader = new StringReader(writer.toString())) {
133           return new XmlSchemaContentValidator(
134               ObjectUtils.notNull(List.of(new StreamSource(reader))));
135         }
136       }
137     }
138 
139     @Override
140     public JsonSchemaContentValidator getJsonSchema(
141         @NonNull JSONObject json,
142         @NonNull IBindingContext bindingContext) throws IOException {
143       IMutableConfiguration<SchemaGenerationFeature<?>> configuration = new DefaultConfiguration<>();
144 
145       try (StringWriter writer = new StringWriter()) {
146         ISchemaGenerator.generateSchema(module, writer, SchemaFormat.JSON, configuration);
147         return new JsonSchemaContentValidator(
148             new JSONObject(new JSONTokener(writer.toString())));
149       }
150     }
151   }
152 }