1   /*
2    * SPDX-FileCopyrightText: none
3    * SPDX-License-Identifier: CC0-1.0
4    */
5   
6   package dev.metaschema.cli.commands;
7   
8   import org.apache.commons.cli.CommandLine;
9   import org.apache.commons.cli.Option;
10  import org.json.JSONObject;
11  import org.json.JSONTokener;
12  import org.xml.sax.SAXException;
13  
14  import java.io.IOException;
15  import java.io.Reader;
16  import java.io.StringReader;
17  import java.io.StringWriter;
18  import java.net.URL;
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.List;
22  import java.util.Set;
23  
24  import javax.xml.transform.stream.StreamSource;
25  
26  import dev.metaschema.cli.processor.CallingContext;
27  import dev.metaschema.cli.processor.command.CommandExecutionException;
28  import dev.metaschema.cli.processor.command.ICommandExecutor;
29  import dev.metaschema.core.configuration.DefaultConfiguration;
30  import dev.metaschema.core.configuration.IMutableConfiguration;
31  import dev.metaschema.core.model.IModule;
32  import dev.metaschema.core.model.constraint.IConstraintSet;
33  import dev.metaschema.core.model.validation.JsonSchemaContentValidator;
34  import dev.metaschema.core.model.validation.XmlSchemaContentValidator;
35  import dev.metaschema.core.util.CollectionUtil;
36  import dev.metaschema.core.util.ObjectUtils;
37  import dev.metaschema.databind.IBindingContext;
38  import dev.metaschema.databind.IBindingContext.ISchemaValidationProvider;
39  import dev.metaschema.schemagen.ISchemaGenerator;
40  import dev.metaschema.schemagen.ISchemaGenerator.SchemaFormat;
41  import dev.metaschema.schemagen.SchemaGenerationFeature;
42  import edu.umd.cs.findbugs.annotations.NonNull;
43  
44  /**
45   * This command implementation supports validation of a content instance based
46   * on a provided Metaschema module.
47   */
48  public class ValidateContentUsingModuleCommand
49      extends AbstractValidateContentCommand {
50    @NonNull
51    private static final String COMMAND = "validate-content";
52  
53    @Override
54    public String getName() {
55      return COMMAND;
56    }
57  
58    @Override
59    public String getDescription() {
60      return "Verify that the provided resource is well-formed and valid to the provided Module-based model.";
61    }
62  
63    @Override
64    public Collection<? extends Option> gatherOptions() {
65      Collection<? extends Option> orig = super.gatherOptions();
66  
67      List<Option> retval = new ArrayList<>(orig.size() + 1);
68      retval.addAll(orig);
69      retval.add(MetaschemaCommands.METASCHEMA_REQUIRED_OPTION);
70  
71      return CollectionUtil.unmodifiableCollection(retval);
72    }
73  
74    @Override
75    public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) {
76      return new CommandExecutor(callingContext, commandLine);
77    }
78  
79    private final class CommandExecutor
80        extends AbstractValidationCommandExecutor {
81  
82      private CommandExecutor(
83          @NonNull CallingContext callingContext,
84          @NonNull CommandLine commandLine) {
85        super(callingContext, commandLine);
86      }
87  
88      @Override
89      protected IBindingContext getBindingContext(@NonNull Set<IConstraintSet> constraintSets)
90          throws CommandExecutionException {
91        return MetaschemaCommands.newBindingContextWithDynamicCompilation(constraintSets);
92      }
93  
94      @SuppressWarnings("synthetic-access")
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 }