001/*
002 * SPDX-FileCopyrightText: none
003 * SPDX-License-Identifier: CC0-1.0
004 */
005
006package dev.metaschema.oscal.tools.cli.core.commands;
007
008import org.apache.commons.cli.CommandLine;
009
010import java.io.FileNotFoundException;
011import java.io.IOException;
012import java.io.InputStream;
013import java.io.Writer;
014import java.net.URI;
015
016import dev.metaschema.cli.commands.AbstractConvertSubcommand;
017import dev.metaschema.cli.processor.CallingContext;
018import dev.metaschema.cli.processor.command.ICommandExecutor;
019import dev.metaschema.core.model.IBoundObject;
020import dev.metaschema.databind.IBindingContext;
021import dev.metaschema.databind.io.Format;
022import dev.metaschema.databind.io.FormatDetector;
023import dev.metaschema.databind.io.IBoundLoader;
024import dev.metaschema.databind.io.ISerializer;
025import dev.metaschema.databind.io.ModelDetector;
026import dev.metaschema.oscal.lib.OscalBindingContext;
027import edu.umd.cs.findbugs.annotations.NonNull;
028
029/**
030 * Provides an OSCAL content conversion command.
031 */
032public class ConvertCommand
033    extends AbstractConvertSubcommand {
034  @Override
035  public String getDescription() {
036    return "Check that the specified OSCAL instance is well-formed and valid to an OSCAL model.";
037  }
038
039  @Override
040  public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) {
041    return new CommandExecutor(callingContext, commandLine);
042  }
043
044  private static final class CommandExecutor
045      extends AbstractConversionCommandExecutor {
046
047    private CommandExecutor(
048        @NonNull CallingContext callingContext,
049        @NonNull CommandLine commandLine) {
050      super(callingContext, commandLine);
051    }
052
053    @Override
054    protected IBindingContext getBindingContext() {
055      return OscalBindingContext.instance();
056    }
057
058    @Override
059    protected void handleConversion(URI source, Format toFormat, Writer writer, IBoundLoader loader)
060        throws FileNotFoundException, IOException {
061
062      Class<? extends IBoundObject> boundClass;
063      IBoundObject object;
064      try (InputStream is = source.toURL().openStream()) {
065        assert is != null;
066        FormatDetector.Result formatResult = loader.detectFormat(is, source);
067        Format sourceformat = formatResult.getFormat();
068        try (InputStream fis = formatResult.getDataStream()) {
069          try (ModelDetector.Result modelResult = loader.detectModel(fis, source, sourceformat)) {
070            boundClass = modelResult.getBoundClass();
071            try (InputStream mis = modelResult.getDataStream()) {
072              object = loader.load(boundClass, sourceformat, mis, source);
073            }
074          }
075        }
076      }
077      ISerializer<?> serializer = getBindingContext().newSerializer(toFormat, boundClass);
078      serializer.serialize(object, writer);
079    }
080  }
081}