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.model.IBoundObject;
12  import gov.nist.secauto.metaschema.core.model.IModule;
13  import gov.nist.secauto.metaschema.core.util.CollectionUtil;
14  import gov.nist.secauto.metaschema.core.util.ObjectUtils;
15  import gov.nist.secauto.metaschema.databind.IBindingContext;
16  import gov.nist.secauto.metaschema.databind.io.Format;
17  import gov.nist.secauto.metaschema.databind.io.FormatDetector;
18  import gov.nist.secauto.metaschema.databind.io.IBoundLoader;
19  import gov.nist.secauto.metaschema.databind.io.IDeserializer;
20  import gov.nist.secauto.metaschema.databind.io.ISerializer;
21  import gov.nist.secauto.metaschema.databind.io.ModelDetector;
22  
23  import org.apache.commons.cli.CommandLine;
24  import org.apache.commons.cli.Option;
25  
26  import java.io.FileNotFoundException;
27  import java.io.IOException;
28  import java.io.InputStream;
29  import java.io.Writer;
30  import java.net.URI;
31  import java.net.URL;
32  import java.util.ArrayList;
33  import java.util.Collection;
34  import java.util.List;
35  
36  import edu.umd.cs.findbugs.annotations.NonNull;
37  
38  /**
39   * This command implementation supports the conversion of a content instance
40   * between supported formats based on a provided Metaschema module.
41   */
42  class ConvertContentUsingModuleCommand
43      extends AbstractConvertSubcommand {
44    @NonNull
45    private static final String COMMAND = "convert";
46  
47    @Override
48    public String getName() {
49      return COMMAND;
50    }
51  
52    @Override
53    public String getDescription() {
54      return "Convert the provided resource aligned to the provided Metaschema module to the specified format.";
55    }
56  
57    @Override
58    public Collection<? extends Option> gatherOptions() {
59      Collection<? extends Option> orig = super.gatherOptions();
60  
61      List<Option> retval = new ArrayList<>(orig.size() + 1);
62      retval.addAll(orig);
63      retval.add(MetaschemaCommands.METASCHEMA_REQUIRED_OPTION);
64  
65      return CollectionUtil.unmodifiableCollection(retval);
66    }
67  
68    @Override
69    public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) {
70  
71      return new CommandExecutor(callingContext, commandLine);
72    }
73  
74    private final class CommandExecutor
75        extends AbstractConversionCommandExecutor {
76  
77      private CommandExecutor(
78          @NonNull CallingContext callingContext,
79          @NonNull CommandLine commandLine) {
80        super(callingContext, commandLine);
81      }
82  
83      @Override
84      protected IBindingContext getBindingContext() throws CommandExecutionException {
85        IBindingContext retval = MetaschemaCommands.newBindingContextWithDynamicCompilation();
86  
87        IModule module = MetaschemaCommands.loadModule(
88            getCommandLine(),
89            MetaschemaCommands.METASCHEMA_REQUIRED_OPTION,
90            ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()),
91            retval);
92        retval.registerModule(module);
93        return retval;
94      }
95  
96      @Override
97      protected void handleConversion(URI source, Format toFormat, Writer writer, IBoundLoader loader)
98          throws FileNotFoundException, IOException {
99        URI resourceUri = loader.resolve(source);
100       URL resource = resourceUri.toURL();
101 
102       try (InputStream is = resource.openStream()) {
103         assert is != null;
104 
105         FormatDetector.Result formatMatch = loader.detectFormat(is);
106         Format format = formatMatch.getFormat();
107 
108         try (InputStream formatStream = formatMatch.getDataStream()) {
109           try (ModelDetector.Result modelMatch = loader.detectModel(formatStream, format)) {
110 
111             IBindingContext bindingContext = loader.getBindingContext();
112 
113             IDeserializer<?> deserializer = bindingContext.newDeserializer(format, modelMatch.getBoundClass());
114             deserializer.applyConfiguration(loader);
115             try (InputStream modelStream = modelMatch.getDataStream()) {
116               IBoundObject obj = deserializer.deserialize(modelStream, resourceUri);
117 
118               ISerializer<?> serializer = bindingContext.newSerializer(toFormat, modelMatch.getBoundClass());
119               serializer.serialize(obj, writer);
120             }
121 
122           }
123         }
124       }
125     }
126 
127   }
128 
129 }