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.json.JSONObject;
10  import org.xml.sax.SAXException;
11  
12  import java.io.BufferedReader;
13  import java.io.IOException;
14  import java.io.InputStream;
15  import java.io.InputStreamReader;
16  import java.net.URL;
17  import java.nio.charset.StandardCharsets;
18  import java.util.LinkedList;
19  import java.util.List;
20  import java.util.Set;
21  
22  import javax.xml.transform.Source;
23  import javax.xml.transform.stream.StreamSource;
24  
25  import dev.metaschema.cli.processor.CallingContext;
26  import dev.metaschema.cli.processor.ExitCode;
27  import dev.metaschema.cli.processor.command.CommandExecutionException;
28  import dev.metaschema.cli.processor.command.ICommandExecutor;
29  import dev.metaschema.core.model.IModule;
30  import dev.metaschema.core.model.MetaschemaException;
31  import dev.metaschema.core.model.constraint.IConstraintSet;
32  import dev.metaschema.core.model.util.JsonUtil;
33  import dev.metaschema.core.model.validation.JsonSchemaContentValidator;
34  import dev.metaschema.core.model.validation.XmlSchemaContentValidator;
35  import dev.metaschema.core.util.ObjectUtils;
36  import dev.metaschema.databind.IBindingContext;
37  import dev.metaschema.databind.IBindingContext.ISchemaValidationProvider;
38  import dev.metaschema.databind.model.metaschema.binding.MetaschemaModelModule;
39  import edu.umd.cs.findbugs.annotations.NonNull;
40  import nl.talsmasoftware.lazy4j.Lazy;
41  
42  /**
43   * This command implementation supports validation of a Metaschema module.
44   */
45  public class ValidateModuleCommand
46      extends AbstractValidateContentCommand {
47    @NonNull
48    private static final String COMMAND = "validate";
49  
50    @Override
51    public String getName() {
52      return COMMAND;
53    }
54  
55    @Override
56    public String getDescription() {
57      return "Validate that the specified Module is well-formed and valid to the Module model";
58    }
59  
60    @Override
61    public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) {
62      return new CommandExecutor(callingContext, commandLine);
63    }
64  
65    private final class CommandExecutor
66        extends AbstractValidationCommandExecutor {
67      private final Lazy<ValidationProvider> validationProvider = Lazy.of(ValidationProvider::new);
68  
69      private CommandExecutor(
70          @NonNull CallingContext callingContext,
71          @NonNull CommandLine commandLine) {
72        super(callingContext, commandLine);
73      }
74  
75      @Override
76      protected IBindingContext getBindingContext(Set<IConstraintSet> constraintSets)
77          throws CommandExecutionException {
78        return MetaschemaCommands.newBindingContextWithDynamicCompilation(constraintSets);
79      }
80  
81      @Override
82      protected IModule getModule(CommandLine commandLine, IBindingContext bindingContext)
83          throws CommandExecutionException {
84        try {
85          return bindingContext.registerModule(MetaschemaModelModule.class);
86        } catch (MetaschemaException ex) {
87          throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex);
88        }
89      }
90  
91      @Override
92      protected ISchemaValidationProvider getSchemaValidationProvider(
93          IModule module,
94          CommandLine commandLine,
95          IBindingContext bindingContext) {
96        // ignore the arguments and return the pre-generated schema provider
97        return ObjectUtils.notNull(validationProvider.get());
98      }
99    }
100 
101   private static final class ValidationProvider implements ISchemaValidationProvider {
102     @SuppressWarnings("resource")
103     @Override
104     public XmlSchemaContentValidator getXmlSchemas(
105         @NonNull URL targetResource,
106         @NonNull IBindingContext bindingContext) throws IOException, SAXException {
107       try (InputStream is = this.getClass().getResourceAsStream("/schema/xml/metaschema-model_schema.xsd")) {
108         List<Source> sources = new LinkedList<>();
109         sources.add(new StreamSource(
110             ObjectUtils.requireNonNull(is,
111                 "Unable to load '/schema/xml/metaschema.xsd' on the classpath")));
112         return new XmlSchemaContentValidator(sources);
113       }
114     }
115 
116     @Override
117     public JsonSchemaContentValidator getJsonSchema(
118         @NonNull JSONObject json,
119         @NonNull IBindingContext bindingContext) throws IOException {
120       try (BufferedReader reader = new BufferedReader(
121           new InputStreamReader(
122               this.getClass().getResourceAsStream("/schema/json/metaschema-model_schema.json"),
123               StandardCharsets.UTF_8))) {
124         return new JsonSchemaContentValidator(JsonUtil.toJsonObject(reader));
125       }
126     }
127   }
128 }