1
2
3
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.ExitCode;
10 import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand;
11 import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException;
12 import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument;
13 import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor;
14 import gov.nist.secauto.metaschema.core.model.IModule;
15 import gov.nist.secauto.metaschema.core.model.util.MermaidErDiagramGenerator;
16 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
17 import gov.nist.secauto.metaschema.databind.IBindingContext;
18
19 import org.apache.commons.cli.CommandLine;
20 import org.apache.commons.cli.Option;
21 import org.apache.logging.log4j.LogManager;
22 import org.apache.logging.log4j.Logger;
23
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.io.StringWriter;
27 import java.io.Writer;
28 import java.net.URI;
29 import java.net.URISyntaxException;
30 import java.nio.charset.StandardCharsets;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import java.nio.file.StandardOpenOption;
34 import java.util.Collection;
35 import java.util.List;
36
37 import edu.umd.cs.findbugs.annotations.NonNull;
38 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
39
40
41
42
43
44 class GenerateDiagramCommand
45 extends AbstractTerminalCommand {
46 private static final Logger LOGGER = LogManager.getLogger(GenerateDiagramCommand.class);
47
48 @NonNull
49 private static final String COMMAND = "generate-diagram";
50 @NonNull
51 private static final List<ExtraArgument> EXTRA_ARGUMENTS;
52
53 static {
54 EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of(
55 ExtraArgument.newInstance("metaschema-module-file-or-URL", true),
56 ExtraArgument.newInstance("destination-diagram-file", false)));
57 }
58
59 @Override
60 public String getName() {
61 return COMMAND;
62 }
63
64 @Override
65 public String getDescription() {
66 return "Generate a diagram for the provided Metaschema module";
67 }
68
69 @SuppressWarnings("null")
70 @Override
71 public Collection<? extends Option> gatherOptions() {
72 return List.of(MetaschemaCommands.OVERWRITE_OPTION);
73 }
74
75 @Override
76 public List<ExtraArgument> getExtraArguments() {
77 return EXTRA_ARGUMENTS;
78 }
79
80 @Override
81 public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine cmdLine) {
82 return ICommandExecutor.using(callingContext, cmdLine, this::executeCommand);
83 }
84
85
86
87
88
89
90
91
92
93
94
95 @SuppressWarnings({
96 "PMD.OnlyOneReturn",
97 "PMD.AvoidCatchingGenericException"
98 })
99 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION",
100 justification = "Catching generic exception for CLI error handling")
101 protected void executeCommand(
102 @NonNull CallingContext callingContext,
103 @NonNull CommandLine cmdLine) throws CommandExecutionException {
104
105 List<String> extraArgs = cmdLine.getArgList();
106
107 Path destination = null;
108 if (extraArgs.size() > 1) {
109 destination = MetaschemaCommands.handleDestination(ObjectUtils.requireNonNull(extraArgs.get(1)), cmdLine);
110 }
111
112 IBindingContext bindingContext = MetaschemaCommands.newBindingContextWithDynamicCompilation();
113
114 URI moduleUri;
115 try {
116 moduleUri = resolveAgainstCWD(ObjectUtils.requireNonNull(extraArgs.get(0)));
117 } catch (URISyntaxException ex) {
118 throw new CommandExecutionException(
119 ExitCode.INVALID_ARGUMENTS,
120 String.format("Cannot load module as '%s' is not a valid file or URL. %s",
121 extraArgs.get(0),
122 ex.getLocalizedMessage()),
123 ex);
124 }
125 IModule module = MetaschemaCommands.loadModule(moduleUri, bindingContext);
126
127 if (destination == null) {
128 Writer stringWriter = new StringWriter();
129 try (PrintWriter writer = new PrintWriter(stringWriter)) {
130 MermaidErDiagramGenerator.generate(module, writer);
131 }
132
133
134 if (LOGGER.isInfoEnabled()) {
135 LOGGER.info(stringWriter.toString());
136 }
137 } else {
138 try (Writer writer = Files.newBufferedWriter(
139 destination,
140 StandardCharsets.UTF_8,
141 StandardOpenOption.CREATE,
142 StandardOpenOption.WRITE,
143 StandardOpenOption.TRUNCATE_EXISTING)) {
144 try (PrintWriter printWriter = new PrintWriter(writer)) {
145 MermaidErDiagramGenerator.generate(module, printWriter);
146 }
147 } catch (IOException ex) {
148 throw new CommandExecutionException(ExitCode.IO_ERROR, ex);
149 }
150 }
151 }
152 }