Skip to content

Commit f7d5a45

Browse files
Added initial implementation of the 'metaschema-cli metapath list-functions' command.
1 parent a0a647d commit f7d5a45

File tree

12 files changed

+281
-19
lines changed

12 files changed

+281
-19
lines changed

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionLibrary.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ private void registerFunctionByName(@NonNull IFunction function) {
8989
}
9090

9191
@Override
92-
public Stream<IFunction> getFunctionsAsStream() {
92+
public Stream<IFunction> stream() {
9393
synchronized (this) {
9494
return ObjectUtils.notNull(libraryByQName.values().stream().flatMap(NamedFunctionSet::getFunctionsAsStream));
9595
}

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/FunctionService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import java.util.ServiceLoader;
3333
import java.util.ServiceLoader.Provider;
34+
import java.util.stream.Stream;
3435

3536
import javax.xml.namespace.QName;
3637

@@ -64,7 +65,7 @@ public FunctionService() {
6465
FunctionLibrary functionLibrary = new FunctionLibrary();
6566
loader.stream()
6667
.map(Provider<IFunctionLibrary>::get)
67-
.flatMap(IFunctionLibrary::getFunctionsAsStream)
68+
.flatMap(IFunctionLibrary::stream)
6869
.forEachOrdered(function -> functionLibrary.registerFunction(ObjectUtils.notNull(function)));
6970
this.library = functionLibrary;
7071
}
@@ -79,6 +80,10 @@ private ServiceLoader<IFunctionLibrary> getLoader() {
7980
return loader;
8081
}
8182

83+
public Stream<IFunction> stream() {
84+
return this.library.stream();
85+
}
86+
8287
/**
8388
* Retrieve the function with the provided name that supports the signature of
8489
* the provided methods, if such a function exists.

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ default String toSignature() {
231231
return ObjectUtils.notNull(String.format("Q{%s}%s(%s) as %s",
232232
getNamespace(),
233233
getName(),
234-
getArguments().isEmpty() ? "()"
234+
getArguments().isEmpty() ? ""
235235
: getArguments().stream().map(IArgument::toSignature).collect(Collectors.joining(","))
236236
+ (isArityUnbounded() ? ", ..." : ""),
237237
getResult().toSignature()));

core/src/main/java/gov/nist/secauto/metaschema/core/metapath/function/IFunctionLibrary.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public interface IFunctionLibrary {
4040
* @return a stream of function signatures
4141
*/
4242
@NonNull
43-
Stream<IFunction> getFunctionsAsStream();
43+
Stream<IFunction> stream();
4444

4545
/**
4646
* Determine if there is a function with the provided name that supports the

databind/src/main/java/gov/nist/secauto/metaschema/databind/io/IBoundLoader.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,4 +523,72 @@ default <CLASS> void convert(
523523
ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
524524
serializer.serialize(object, os);
525525
}
526+
527+
/**
528+
* Auto convert the provided {@code source} to the provided {@code toFormat}.
529+
* Write the converted content to the provided {@code destination}.
530+
* <p>
531+
* The format of the source is expected to be auto detected using
532+
* {@link #detectFormat(Path)}.
533+
*
534+
* @param <CLASS>
535+
* the Java type to load data into
536+
* @param source
537+
* the resource to convert
538+
* @param destination
539+
* the resource to write converted content to
540+
* @param toFormat
541+
* the format to convert to
542+
* @param rootClass
543+
* the class for the Java type to load data into
544+
* @throws FileNotFoundException
545+
* the the provided source file was not found
546+
* @throws IOException
547+
* if an error occurred while loading the data from the specified
548+
* resource or writing the converted data to the specified destination
549+
*/
550+
default <CLASS> void convert(
551+
@NonNull URI source,
552+
@NonNull Path destination,
553+
@NonNull Format toFormat,
554+
@NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
555+
CLASS object = load(rootClass, source);
556+
557+
ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
558+
serializer.serialize(object, destination);
559+
}
560+
561+
/**
562+
* Auto convert the provided {@code source} to the provided {@code toFormat}.
563+
* Write the converted content to the provided {@code destination}.
564+
* <p>
565+
* The format of the source is expected to be auto detected using
566+
* {@link #detectFormat(Path)}.
567+
*
568+
* @param <CLASS>
569+
* the Java type to load data into
570+
* @param source
571+
* the resource to convert
572+
* @param os
573+
* the output stream to write converted content to
574+
* @param toFormat
575+
* the format to convert to
576+
* @param rootClass
577+
* the class for the Java type to load data into
578+
* @throws FileNotFoundException
579+
* the the provided source file was not found
580+
* @throws IOException
581+
* if an error occurred while loading the data from the specified
582+
* resource or writing the converted data to the specified destination
583+
*/
584+
default <CLASS> void convert(
585+
@NonNull URI source,
586+
@NonNull OutputStream os,
587+
@NonNull Format toFormat,
588+
@NonNull Class<CLASS> rootClass) throws FileNotFoundException, IOException {
589+
CLASS object = load(rootClass, source);
590+
591+
ISerializer<CLASS> serializer = getBindingContext().newSerializer(toFormat, rootClass);
592+
serializer.serialize(object, os);
593+
}
526594
}

metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/CLI.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import gov.nist.secauto.metaschema.cli.commands.GenerateSchemaCommand;
3030
import gov.nist.secauto.metaschema.cli.commands.ValidateContentUsingModuleCommand;
3131
import gov.nist.secauto.metaschema.cli.commands.ValidateModuleCommand;
32+
import gov.nist.secauto.metaschema.cli.commands.metapath.MetapathCommand;
3233
import gov.nist.secauto.metaschema.cli.processor.CLIProcessor;
3334
import gov.nist.secauto.metaschema.cli.processor.ExitStatus;
3435
import gov.nist.secauto.metaschema.cli.processor.command.CommandService;
@@ -59,6 +60,7 @@ public static ExitStatus runCli(String... args) {
5960
processor.addCommandHandler(new ValidateModuleCommand());
6061
processor.addCommandHandler(new GenerateSchemaCommand());
6162
processor.addCommandHandler(new ValidateContentUsingModuleCommand());
63+
processor.addCommandHandler(new MetapathCommand());
6264

6365
CommandService.getInstance().getCommands().stream().forEach(command -> {
6466
assert command != null;

metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractConvertSubcommand.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import gov.nist.secauto.metaschema.cli.processor.command.ExtraArgument;
3838
import gov.nist.secauto.metaschema.core.util.CustomCollectors;
3939
import gov.nist.secauto.metaschema.core.util.ObjectUtils;
40+
import gov.nist.secauto.metaschema.core.util.UriUtils;
4041
import gov.nist.secauto.metaschema.databind.IBindingContext;
4142
import gov.nist.secauto.metaschema.databind.io.Format;
4243
import gov.nist.secauto.metaschema.databind.io.IBoundLoader;
@@ -47,6 +48,8 @@
4748
import org.apache.logging.log4j.Logger;
4849

4950
import java.io.IOException;
51+
import java.net.URI;
52+
import java.net.URISyntaxException;
5053
import java.nio.file.Files;
5154
import java.nio.file.Path;
5255
import java.nio.file.Paths;
@@ -64,8 +67,8 @@ public abstract class AbstractConvertSubcommand
6467
private static final String COMMAND = "convert";
6568
@NonNull
6669
private static final List<ExtraArgument> EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of(
67-
new DefaultExtraArgument("source file", true),
68-
new DefaultExtraArgument("destination file", false)));
70+
new DefaultExtraArgument("source-file-or-URL", true),
71+
new DefaultExtraArgument("destination-file", false)));
6972

7073
@NonNull
7174
private static final Option OVERWRITE_OPTION = ObjectUtils.notNull(
@@ -121,14 +124,6 @@ public void validateOptions(CallingContext callingContext, CommandLine cmdLine)
121124
if (extraArgs.isEmpty() || extraArgs.size() > 2) {
122125
throw new InvalidArgumentException("Illegal number of arguments.");
123126
}
124-
125-
Path source = Paths.get(extraArgs.get(0));
126-
if (!Files.exists(source)) {
127-
throw new InvalidArgumentException("The provided source '" + source + "' does not exist.");
128-
}
129-
if (!Files.isReadable(source)) {
130-
throw new InvalidArgumentException("The provided source '" + source + "' is not readable.");
131-
}
132127
}
133128

134129
protected abstract static class AbstractConversionCommandExecutor
@@ -185,7 +180,15 @@ public ExitStatus execute() {
185180
}
186181
}
187182

188-
Path source = Paths.get(extraArgs.get(0));
183+
String sourceName = extraArgs.get(0);
184+
URI source;
185+
URI cwd = Paths.get("").toAbsolutePath().toUri();
186+
try {
187+
source = UriUtils.toUri(sourceName, cwd);
188+
} catch (URISyntaxException ex) {
189+
return ExitCode.IO_ERROR.exitMessage("Cannot load source '%s' as it is not a valid file or URI.")
190+
.withThrowable(ex);
191+
}
189192
assert source != null;
190193

191194
String toFormatText = cmdLine.getOptionValue(TO_OPTION);

metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/AbstractValidateContentCommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public abstract class AbstractValidateContentCommand
8080
private static final String COMMAND = "validate";
8181
@NonNull
8282
private static final List<ExtraArgument> EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of(
83-
new DefaultExtraArgument("file to validate", true)));
83+
new DefaultExtraArgument("file-or-URI-to-validate", true)));
8484

8585
@NonNull
8686
private static final Option AS_OPTION = ObjectUtils.notNull(
@@ -189,7 +189,7 @@ public ExitStatus execute() {
189189
IBoundLoader loader = bindingContext.newBoundLoader();
190190

191191
List<String> extraArgs = cmdLine.getArgList();
192-
// @SuppressWarnings("null")
192+
193193
String sourceName = extraArgs.get(0);
194194
URI source;
195195

metaschema-cli/src/main/java/gov/nist/secauto/metaschema/cli/commands/GenerateSchemaCommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public class GenerateSchemaCommand
9999

100100
static {
101101
EXTRA_ARGUMENTS = ObjectUtils.notNull(List.of(
102-
new DefaultExtraArgument("metaschema-module-file", true),
102+
new DefaultExtraArgument("metaschema-module-file-or-URL", true),
103103
new DefaultExtraArgument("destination-schema-file", false)));
104104
}
105105

@@ -166,7 +166,6 @@ protected ExitStatus executeCommand(
166166
@NonNull CallingContext callingContext,
167167
@NonNull CommandLine cmdLine) {
168168
List<String> extraArgs = cmdLine.getArgList();
169-
URI cwd = Paths.get("").toAbsolutePath().toUri();
170169

171170
Path destination = null;
172171
if (extraArgs.size() > 1) {
@@ -212,6 +211,7 @@ protected ExitStatus executeCommand(
212211

213212
URI input;
214213
String inputName = extraArgs.get(0);
214+
URI cwd = Paths.get("").toAbsolutePath().toUri();
215215

216216
try {
217217
input = UriUtils.toUri(extraArgs.get(0), cwd);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Portions of this software was developed by employees of the National Institute
3+
* of Standards and Technology (NIST), an agency of the Federal Government and is
4+
* being made available as a public service. Pursuant to title 17 United States
5+
* Code Section 105, works of NIST employees are not subject to copyright
6+
* protection in the United States. This software may be subject to foreign
7+
* copyright. Permission in the United States and in foreign countries, to the
8+
* extent that NIST may hold copyright, to use, copy, modify, create derivative
9+
* works, and distribute this software and its documentation without fee is hereby
10+
* granted on a non-exclusive basis, provided that this notice and disclaimer
11+
* of warranty appears in all copies.
12+
*
13+
* THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
14+
* EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
15+
* THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
16+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
17+
* INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
18+
* SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE. IN NO EVENT
19+
* SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
20+
* INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
21+
* OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
22+
* CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
23+
* PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
24+
* OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
25+
*/
26+
27+
package gov.nist.secauto.metaschema.cli.commands.metapath;
28+
29+
import gov.nist.secauto.metaschema.cli.processor.CLIProcessor.CallingContext;
30+
import gov.nist.secauto.metaschema.cli.processor.ExitCode;
31+
import gov.nist.secauto.metaschema.cli.processor.ExitStatus;
32+
import gov.nist.secauto.metaschema.cli.processor.command.AbstractTerminalCommand;
33+
import gov.nist.secauto.metaschema.cli.processor.command.ICommandExecutor;
34+
import gov.nist.secauto.metaschema.core.metapath.StaticContext;
35+
import gov.nist.secauto.metaschema.core.metapath.function.FunctionService;
36+
import gov.nist.secauto.metaschema.core.metapath.function.IArgument;
37+
import gov.nist.secauto.metaschema.core.metapath.function.IFunction;
38+
39+
import org.apache.commons.cli.CommandLine;
40+
import org.apache.logging.log4j.LogManager;
41+
import org.apache.logging.log4j.Logger;
42+
43+
import java.util.ArrayList;
44+
import java.util.Collections;
45+
import java.util.Comparator;
46+
import java.util.List;
47+
import java.util.Map;
48+
import java.util.stream.Collectors;
49+
50+
import edu.umd.cs.findbugs.annotations.NonNull;
51+
52+
public class ListFunctionsSubcommand
53+
extends AbstractTerminalCommand {
54+
private static final Logger LOGGER = LogManager.getLogger(ListFunctionsSubcommand.class);
55+
56+
@NonNull
57+
private static final String COMMAND = "list-functions";
58+
59+
@Override
60+
public String getName() {
61+
return COMMAND;
62+
}
63+
64+
@Override
65+
public String getDescription() {
66+
return "Get a listing of supported Metapath functions";
67+
}
68+
69+
@Override
70+
public ICommandExecutor newExecutor(CallingContext callingContext, CommandLine cmdLine) {
71+
return ICommandExecutor.using(callingContext, cmdLine, this::executeCommand);
72+
}
73+
74+
@SuppressWarnings({
75+
"PMD.OnlyOneReturn", // readability
76+
"unused"
77+
})
78+
protected ExitStatus executeCommand(
79+
@NonNull CallingContext callingContext,
80+
@NonNull CommandLine cmdLine) {
81+
82+
Map<String, Map<String, List<IFunction>>> namespaceToNameToFunctionMap = FunctionService.getInstance().stream()
83+
.collect(Collectors.groupingBy(
84+
IFunction::getNamespace,
85+
Collectors.groupingBy(
86+
IFunction::getName,
87+
Collectors.toList())));
88+
89+
Map<String, String> namespaceToPrefixMap = StaticContext.getWellKnownNamespaces().entrySet().stream()
90+
.collect(Collectors.toMap(entry -> entry.getValue().toASCIIString(), Map.Entry::getKey));
91+
92+
List<String> namespaces = new ArrayList<>(namespaceToNameToFunctionMap.keySet());
93+
94+
Collections.sort(namespaces);
95+
96+
for (String namespace : namespaces) {
97+
String prefix = namespaceToPrefixMap.get(namespace);
98+
99+
if (prefix == null) {
100+
LOGGER.atInfo().log("In namespace '{}':", namespace);
101+
} else {
102+
LOGGER.atInfo().log("In namespace '{}' as '{}':", namespace, prefix);
103+
}
104+
105+
Map<String, List<IFunction>> namespacedFunctions = namespaceToNameToFunctionMap.get(namespace);
106+
107+
List<String> names = new ArrayList<>(namespacedFunctions.keySet());
108+
Collections.sort(names);
109+
110+
for (String name : names) {
111+
List<IFunction> functions = namespacedFunctions.get(name);
112+
Collections.sort(functions, Comparator.comparing(IFunction::arity));
113+
114+
for (IFunction function : functions) {
115+
String functionRef = prefix == null
116+
? String.format("Q{%s}%s", function.getNamespace(), function.getName())
117+
: String.format("%s:%s", prefix, function.getName());
118+
119+
LOGGER.atInfo().log(String.format("%s(%s) as %s",
120+
functionRef,
121+
function.getArguments().isEmpty()
122+
? ""
123+
: function.getArguments().stream().map(IArgument::toSignature)
124+
.collect(Collectors.joining(","))
125+
+ (function.isArityUnbounded() ? ", ..." : ""),
126+
function.getResult().toSignature()));
127+
}
128+
}
129+
}
130+
return ExitCode.OK.exit();
131+
}
132+
}

0 commit comments

Comments
 (0)