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