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