001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.cli.commands;
007
008import org.apache.commons.cli.CommandLine;
009import org.json.JSONObject;
010import org.xml.sax.SAXException;
011
012import java.io.BufferedReader;
013import java.io.IOException;
014import java.io.InputStream;
015import java.io.InputStreamReader;
016import java.net.URL;
017import java.nio.charset.StandardCharsets;
018import java.util.LinkedList;
019import java.util.List;
020import java.util.Set;
021
022import javax.xml.transform.Source;
023import javax.xml.transform.stream.StreamSource;
024
025import dev.metaschema.cli.processor.CallingContext;
026import dev.metaschema.cli.processor.ExitCode;
027import dev.metaschema.cli.processor.command.CommandExecutionException;
028import dev.metaschema.cli.processor.command.ICommandExecutor;
029import dev.metaschema.core.model.IModule;
030import dev.metaschema.core.model.MetaschemaException;
031import dev.metaschema.core.model.constraint.IConstraintSet;
032import dev.metaschema.core.model.util.JsonUtil;
033import dev.metaschema.core.model.validation.JsonSchemaContentValidator;
034import dev.metaschema.core.model.validation.XmlSchemaContentValidator;
035import dev.metaschema.core.util.ObjectUtils;
036import dev.metaschema.databind.IBindingContext;
037import dev.metaschema.databind.IBindingContext.ISchemaValidationProvider;
038import dev.metaschema.databind.model.metaschema.binding.MetaschemaModelModule;
039import edu.umd.cs.findbugs.annotations.NonNull;
040import nl.talsmasoftware.lazy4j.Lazy;
041
042/**
043 * This command implementation supports validation of a Metaschema module.
044 */
045public class ValidateModuleCommand
046    extends AbstractValidateContentCommand {
047  @NonNull
048  private static final String COMMAND = "validate";
049
050  @Override
051  public String getName() {
052    return COMMAND;
053  }
054
055  @Override
056  public String getDescription() {
057    return "Validate that the specified Module is well-formed and valid to the Module model";
058  }
059
060  @Override
061  public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) {
062    return new CommandExecutor(callingContext, commandLine);
063  }
064
065  private final class CommandExecutor
066      extends AbstractValidationCommandExecutor {
067    private final Lazy<ValidationProvider> validationProvider = Lazy.of(ValidationProvider::new);
068
069    private CommandExecutor(
070        @NonNull CallingContext callingContext,
071        @NonNull CommandLine commandLine) {
072      super(callingContext, commandLine);
073    }
074
075    @Override
076    protected IBindingContext getBindingContext(Set<IConstraintSet> constraintSets)
077        throws CommandExecutionException {
078      return MetaschemaCommands.newBindingContextWithDynamicCompilation(constraintSets);
079    }
080
081    @Override
082    protected IModule getModule(CommandLine commandLine, IBindingContext bindingContext)
083        throws CommandExecutionException {
084      try {
085        return bindingContext.registerModule(MetaschemaModelModule.class);
086      } catch (MetaschemaException ex) {
087        throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex);
088      }
089    }
090
091    @Override
092    protected ISchemaValidationProvider getSchemaValidationProvider(
093        IModule module,
094        CommandLine commandLine,
095        IBindingContext bindingContext) {
096      // ignore the arguments and return the pre-generated schema provider
097      return ObjectUtils.notNull(validationProvider.get());
098    }
099  }
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}