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