1
2
3
4
5
6 package gov.nist.secauto.metaschema.cli.commands.metapath;
7
8 import gov.nist.secauto.metaschema.cli.commands.MetaschemaCommands;
9 import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext;
10 import gov.nist.secauto.metaschema.cli.processor.ExitCode;
11 import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand;
12 import gov.nist.secauto.metaschema.cli.processor.command.CommandExecutionException;
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.metapath.DynamicContext;
16 import gov.nist.secauto.metaschema.core.metapath.IMetapathExpression;
17 import gov.nist.secauto.metaschema.core.metapath.StaticContext;
18 import gov.nist.secauto.metaschema.core.metapath.item.DefaultItemWriter;
19 import gov.nist.secauto.metaschema.core.metapath.item.IItemWriter;
20 import gov.nist.secauto.metaschema.core.metapath.item.ISequence;
21 import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItem;
22 import gov.nist.secauto.metaschema.core.metapath.item.node.INodeItemFactory;
23 import gov.nist.secauto.metaschema.core.model.IModule;
24 import gov.nist.secauto.metaschema.core.model.MetaschemaException;
25 import gov.nist.secauto.metaschema.core.util.CollectionUtil;
26 import gov.nist.secauto.metaschema.core.util.ObjectUtils;
27 import gov.nist.secauto.metaschema.databind.IBindingContext;
28 import gov.nist.secauto.metaschema.databind.io.IBoundLoader;
29
30 import org.apache.commons.cli.CommandLine;
31 import org.apache.commons.cli.Option;
32 import org.apache.logging.log4j.LogManager;
33 import org.apache.logging.log4j.Logger;
34
35 import java.io.IOException;
36 import java.io.PrintWriter;
37 import java.io.StringWriter;
38 import java.io.Writer;
39 import java.net.URI;
40 import java.net.URISyntaxException;
41 import java.util.Collection;
42 import java.util.List;
43
44 import edu.umd.cs.findbugs.annotations.NonNull;
45 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 class EvaluateMetapathCommand
62 extends AbstractTerminalCommand {
63 private static final Logger LOGGER = LogManager.getLogger(EvaluateMetapathCommand.class);
64
65 @NonNull
66 private static final String COMMAND = "eval";
67 @NonNull
68 private static final Option EXPRESSION_OPTION = ObjectUtils.notNull(
69 Option.builder("e")
70 .longOpt("expression")
71 .required()
72 .hasArg()
73 .argName("EXPRESSION")
74 .desc("Metapath expression to execute")
75 .get());
76 @NonNull
77 private static final Option CONTENT_OPTION = ObjectUtils.notNull(
78 Option.builder("i")
79 .hasArg()
80 .argName("FILE_OR_URL")
81 .desc("Metaschema content instance resource")
82 .get());
83
84 @Override
85 public String getName() {
86 return COMMAND;
87 }
88
89 @Override
90 public String getDescription() {
91 return "Execute a Metapath expression against a document";
92 }
93
94 @SuppressWarnings("null")
95 @Override
96 public Collection<? extends Option> gatherOptions() {
97 return List.of(
98 MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION,
99 CONTENT_OPTION,
100 EXPRESSION_OPTION);
101 }
102
103 @Override
104 public List<ExtraArgument> getExtraArguments() {
105 return CollectionUtil.emptyList();
106 }
107
108 @Override
109 public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine cmdLine) {
110 return ICommandExecutor.using(callingContext, cmdLine, this::executeCommand);
111 }
112
113 @SuppressWarnings({
114 "PMD.OnlyOneReturn",
115 "PMD.AvoidCatchingGenericException",
116 "PMD.NPathComplexity",
117 "PMD.CognitiveComplexity",
118 "PMD.CyclomaticComplexity"
119 })
120 @SuppressFBWarnings(value = "REC_CATCH_EXCEPTION",
121 justification = "Catching generic exception for CLI error handling")
122 private void executeCommand(
123 @SuppressWarnings("unused") @NonNull CallingContext callingContext,
124 @NonNull CommandLine cmdLine) throws CommandExecutionException {
125
126 IModule module = null;
127 INodeItem item = null;
128 if (cmdLine.hasOption(MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION)) {
129 IBindingContext bindingContext = MetaschemaCommands.newBindingContextWithDynamicCompilation();
130
131 try {
132 module = bindingContext.registerModule(MetaschemaCommands.loadModule(
133 cmdLine,
134 MetaschemaCommands.METASCHEMA_OPTIONAL_OPTION,
135 ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()),
136 bindingContext));
137 } catch (MetaschemaException ex) {
138 throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex);
139 }
140
141
142 if (cmdLine.hasOption(CONTENT_OPTION)) {
143
144
145 IBoundLoader loader = bindingContext.newBoundLoader();
146
147 String contentLocation = ObjectUtils.requireNonNull(cmdLine.getOptionValue(CONTENT_OPTION));
148 URI contentResource;
149 try {
150 contentResource = MetaschemaCommands.getResourceUri(
151 contentLocation,
152 ObjectUtils.notNull(getCurrentWorkingDirectory().toUri()));
153 } catch (URISyntaxException ex) {
154 throw new CommandExecutionException(
155 ExitCode.INVALID_ARGUMENTS,
156 String.format("Unable to load content '%s'. %s",
157 contentLocation,
158 ex.getMessage()),
159 ex);
160 }
161
162 try {
163 item = loader.loadAsNodeItem(contentResource);
164 } catch (IOException ex) {
165 throw new CommandExecutionException(
166 ExitCode.INVALID_ARGUMENTS,
167 String.format("Unable to load content '%s'. %s",
168 contentLocation,
169 ex.getMessage()),
170 ex);
171 }
172 } else {
173
174 item = INodeItemFactory.instance().newModuleNodeItem(module);
175 }
176 } else if (cmdLine.hasOption(CONTENT_OPTION)) {
177
178 throw new CommandExecutionException(
179 ExitCode.INVALID_ARGUMENTS,
180 String.format("Must use '%s' to specify the Metaschema module.",
181 CONTENT_OPTION.getArgName()));
182 }
183
184
185 StaticContext.Builder builder = StaticContext.builder();
186 if (module != null) {
187 builder.defaultModelNamespace(module.getXmlNamespace());
188 }
189 StaticContext staticContext = builder.build();
190
191 String expression = cmdLine.getOptionValue(EXPRESSION_OPTION);
192 if (expression == null) {
193 throw new CommandExecutionException(
194 ExitCode.INVALID_ARGUMENTS,
195 String.format("Must use '%s' to specify the Metapath expression.", EXPRESSION_OPTION.getArgName()));
196 }
197
198 try {
199
200 ISequence<?> sequence = IMetapathExpression.compile(expression, staticContext)
201 .evaluate(item, new DynamicContext(staticContext));
202
203
204 try (Writer stringWriter = new StringWriter()) {
205 try (PrintWriter writer = new PrintWriter(stringWriter)) {
206 try (IItemWriter itemWriter = new DefaultItemWriter(writer)) {
207 itemWriter.writeSequence(sequence);
208 } catch (IOException ex) {
209 throw new CommandExecutionException(ExitCode.IO_ERROR, ex);
210 } catch (Exception ex) {
211 throw new CommandExecutionException(ExitCode.RUNTIME_ERROR, ex);
212 }
213 }
214
215
216 if (LOGGER.isInfoEnabled()) {
217 LOGGER.info(stringWriter.toString());
218 }
219 } catch (IOException ex) {
220 throw new CommandExecutionException(ExitCode.IO_ERROR, ex);
221 }
222 } catch (RuntimeException ex) {
223 throw new CommandExecutionException(ExitCode.PROCESSING_ERROR, ex);
224 }
225 }
226 }