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.apache.commons.cli.Option; 010 011import java.io.FileNotFoundException; 012import java.io.IOException; 013import java.io.InputStream; 014import java.io.Writer; 015import java.net.URI; 016import java.net.URL; 017import java.util.ArrayList; 018import java.util.Collection; 019import java.util.List; 020 021import dev.metaschema.cli.processor.CallingContext; 022import dev.metaschema.cli.processor.command.CommandExecutionException; 023import dev.metaschema.cli.processor.command.ICommandExecutor; 024import dev.metaschema.core.model.IBoundObject; 025import dev.metaschema.core.model.IModule; 026import dev.metaschema.core.model.MetaschemaException; 027import dev.metaschema.core.util.CollectionUtil; 028import dev.metaschema.core.util.ObjectUtils; 029import dev.metaschema.databind.IBindingContext; 030import dev.metaschema.databind.io.Format; 031import dev.metaschema.databind.io.FormatDetector; 032import dev.metaschema.databind.io.IBoundLoader; 033import dev.metaschema.databind.io.IDeserializer; 034import dev.metaschema.databind.io.ISerializer; 035import dev.metaschema.databind.io.ModelDetector; 036import edu.umd.cs.findbugs.annotations.NonNull; 037 038/** 039 * This command implementation supports the conversion of a content instance 040 * between supported formats based on a provided Metaschema module. 041 */ 042public class ConvertContentUsingModuleCommand 043 extends AbstractConvertSubcommand { 044 @NonNull 045 private static final String COMMAND = "convert"; 046 047 @Override 048 public String getName() { 049 return COMMAND; 050 } 051 052 @Override 053 public String getDescription() { 054 return "Convert the provided resource aligned to the provided Metaschema module to the specified format."; 055 } 056 057 @Override 058 public Collection<? extends Option> gatherOptions() { 059 Collection<? extends Option> orig = super.gatherOptions(); 060 061 List<Option> retval = new ArrayList<>(orig.size() + 1); 062 retval.addAll(orig); 063 retval.add(MetaschemaCommands.METASCHEMA_REQUIRED_OPTION); 064 065 return CollectionUtil.unmodifiableCollection(retval); 066 } 067 068 @Override 069 public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine commandLine) { 070 071 return new CommandExecutor(callingContext, commandLine); 072 } 073 074 private final class CommandExecutor 075 extends AbstractConversionCommandExecutor { 076 077 private CommandExecutor( 078 @NonNull CallingContext callingContext, 079 @NonNull CommandLine commandLine) { 080 super(callingContext, commandLine); 081 } 082 083 @Override 084 protected IBindingContext getBindingContext() throws CommandExecutionException, MetaschemaException { 085 IBindingContext retval = MetaschemaCommands.newBindingContextWithDynamicCompilation(); 086 087 @SuppressWarnings("synthetic-access") 088 IModule module = MetaschemaCommands.loadModule( 089 getCommandLine(), 090 MetaschemaCommands.METASCHEMA_REQUIRED_OPTION, 091 ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()), 092 retval); 093 retval.registerModule(module); 094 return retval; 095 } 096 097 @Override 098 protected void handleConversion(URI source, Format toFormat, Writer writer, IBoundLoader loader) 099 throws FileNotFoundException, IOException { 100 URI resourceUri = loader.resolve(source); 101 URL resource = resourceUri.toURL(); 102 103 try (InputStream is = resource.openStream()) { 104 assert is != null; 105 106 FormatDetector.Result formatMatch = loader.detectFormat(is, resourceUri); 107 Format format = formatMatch.getFormat(); 108 109 try (InputStream formatStream = formatMatch.getDataStream()) { 110 try (ModelDetector.Result modelMatch = loader.detectModel(formatStream, resourceUri, format)) { 111 112 IBindingContext bindingContext = loader.getBindingContext(); 113 114 IDeserializer<?> deserializer = bindingContext.newDeserializer(format, modelMatch.getBoundClass()); 115 deserializer.applyConfiguration(loader); 116 try (InputStream modelStream = modelMatch.getDataStream()) { 117 IBoundObject obj = deserializer.deserialize(modelStream, resourceUri); 118 119 ISerializer<?> serializer = bindingContext.newSerializer(toFormat, modelMatch.getBoundClass()); 120 serializer.serialize(obj, writer); 121 } 122 123 } 124 } 125 } 126 } 127 128 } 129 130}