Skip to content

Commit

Permalink
add experimental module support
Browse files Browse the repository at this point in the history
  • Loading branch information
eiiches committed Sep 27, 2021
1 parent 567bf84 commit d3a2b7e
Show file tree
Hide file tree
Showing 26 changed files with 1,001 additions and 39 deletions.
10 changes: 10 additions & 0 deletions jackson-jq-cli/src/main/java/net/thisptr/jackson/jq/cli/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.FileSystems;
import java.util.Arrays;
import java.util.List;

Expand All @@ -28,6 +29,10 @@
import net.thisptr.jackson.jq.Versions;
import net.thisptr.jackson.jq.exception.JsonQueryException;
import net.thisptr.jackson.jq.internal.functions.EnvFunction;
import net.thisptr.jackson.jq.module.ModuleLoader;
import net.thisptr.jackson.jq.module.loaders.BuiltinModuleLoader;
import net.thisptr.jackson.jq.module.loaders.ChainedModuleLoader;
import net.thisptr.jackson.jq.module.loaders.FileSystemModuleLoader;

public class Main {
private static final ObjectMapper MAPPER = new ObjectMapper();
Expand Down Expand Up @@ -108,6 +113,11 @@ public static void main(String[] args) throws IOException, ParseException {
BuiltinFunctionLoader.getInstance().loadFunctions(version, scope);
scope.addFunction("env", 0, new EnvFunction());

scope.setModuleLoader(new ChainedModuleLoader(new ModuleLoader[] {
BuiltinModuleLoader.getInstance(),
new FileSystemModuleLoader(scope, version, FileSystems.getDefault().getPath("").toAbsolutePath()),
}));

try (final BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
final JsonParser parser = MAPPER.getFactory().createParser(reader);
while (!parser.isClosed()) {
Expand Down
2 changes: 1 addition & 1 deletion jackson-jq-extra/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
<Include-Resource>META-INF/services/net.thisptr.jackson.jq.Function=${project.build.outputDirectory}/META-INF/services/net.thisptr.jackson.jq.Function</Include-Resource>
<Include-Resource>META-INF/services/net.thisptr.jackson.jq.Function=${project.build.outputDirectory}/META-INF/services/net.thisptr.jackson.jq.Function,META-INF/services/net.thisptr.jackson.jq.module.Module=${project.build.outputDirectory}/META-INF/services/net.thisptr.jackson.jq.module.Module</Include-Resource>
</instructions>
</configuration>
</plugin>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package net.thisptr.jackson.jq.extra;

import com.google.auto.service.AutoService;

import net.thisptr.jackson.jq.BuiltinFunction;
import net.thisptr.jackson.jq.Function;
import net.thisptr.jackson.jq.extra.functions.HostnameFunction;
import net.thisptr.jackson.jq.extra.functions.RandomFunction;
import net.thisptr.jackson.jq.extra.functions.StrFTimeFunction;
import net.thisptr.jackson.jq.extra.functions.StrPTimeFunction;
import net.thisptr.jackson.jq.extra.functions.TimestampFunction;
import net.thisptr.jackson.jq.extra.functions.UriDecodeFunction;
import net.thisptr.jackson.jq.extra.functions.UriParseFunction;
import net.thisptr.jackson.jq.extra.functions.Uuid4Function;
import net.thisptr.jackson.jq.module.BuiltinModule;
import net.thisptr.jackson.jq.module.Module;
import net.thisptr.jackson.jq.module.SimpleModule;

@AutoService(Module.class)
@BuiltinModule(path = "jackson-jq/extras")
public class ModuleImpl extends SimpleModule {

public ModuleImpl() {
addFunction(new HostnameFunction());
addFunction(new RandomFunction());
addFunction(new StrFTimeFunction());
addFunction(new StrPTimeFunction());
addFunction(new TimestampFunction());
addFunction(new UriDecodeFunction());
addFunction(new UriParseFunction());
addFunction(new Uuid4Function());
}

private void addFunction(final Function f) {
final BuiltinFunction annotation = f.getClass().getAnnotation(BuiltinFunction.class);
for (final String fname : annotation.value())
addFunction(fname, f);
}
}
82 changes: 82 additions & 0 deletions jackson-jq/src/main/java/net/thisptr/jackson/jq/Scope.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package net.thisptr.jackson.jq;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
Expand All @@ -11,6 +14,9 @@
import com.fasterxml.jackson.databind.ObjectMapper;

import net.thisptr.jackson.jq.internal.misc.JsonQueryJacksonModule;
import net.thisptr.jackson.jq.internal.module.loaders.NullModuleLoader;
import net.thisptr.jackson.jq.module.Module;
import net.thisptr.jackson.jq.module.ModuleLoader;
import net.thisptr.jackson.jq.path.Path;

public class Scope {
Expand All @@ -31,6 +37,15 @@ private Map<String, String> debugFunctions() {
@JsonIgnore
private Map<String, Function> functions;

@JsonIgnore
private Map<String, LinkedList<Module>> importedModules; // the last import comes first; the key is null when the module is loaded by an include statement.

@JsonIgnore
private Map<String, JsonNode> importedData; // the last import overwrites prior imports

@JsonIgnore
private ModuleLoader moduleLoader;

public interface ValueWithPath {
JsonNode value();

Expand Down Expand Up @@ -66,6 +81,8 @@ public Path path() {
@JsonIgnore
private ObjectMapper mapper = DEFAULT_MAPPER;

private Module currentModule;

private Scope(final Scope parentScope) {
this.parentScope = parentScope;
}
Expand Down Expand Up @@ -148,4 +165,69 @@ public JsonNode getValue(final String name) {
public ObjectMapper getObjectMapper() {
return mapper;
}

public void setImportedData(final String name, final JsonNode data) {
if (importedData == null)
importedData = new HashMap<>();
importedData.put(name, data);
}

public JsonNode getImportedData(final String name) {
if (importedData != null) {
final JsonNode data = importedData.get(name);
if (data != null)
return data;
}
if (parentScope == null)
return null;
return parentScope.getImportedData(name);
}

public void addImportedModule(final String name, final Module module) {
if (importedModules == null)
importedModules = new HashMap<>();
importedModules.computeIfAbsent(name, (dummy) -> new LinkedList<>()).addFirst(module);
}

public List<Module> getImportedModules(final String name) { // the last import comes first
final List<Module> modules = new ArrayList<>();
getImportedModules(modules, name);
return modules;
}

private void getImportedModules(final List<Module> modules, final String name) {
if (importedModules != null) {
final List<Module> localModules = importedModules.get(name);
if (localModules != null) {
modules.addAll(localModules);
}
}
if (parentScope == null)
return;
parentScope.getImportedModules(modules, name);
}

public void setModuleLoader(final ModuleLoader moduleLoader) {
this.moduleLoader = moduleLoader;
}

public ModuleLoader getModuleLoader() {
if (this.moduleLoader != null)
return this.moduleLoader;
if (parentScope == null)
return NullModuleLoader.getInstance();
return parentScope.getModuleLoader();
}

public Module getCurrentModule() {
if (this.currentModule != null)
return this.currentModule;
if (parentScope == null)
return null;
return parentScope.getCurrentModule();
}

public void setCurrentModule(final Module module) {
this.currentModule = module;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package net.thisptr.jackson.jq.internal.module.loaders;

import com.fasterxml.jackson.databind.JsonNode;

import net.thisptr.jackson.jq.module.Module;
import net.thisptr.jackson.jq.module.ModuleLoader;

public class NullModuleLoader implements ModuleLoader {

private static final NullModuleLoader INSTANCE = new NullModuleLoader();

public static NullModuleLoader getInstance() {
return INSTANCE;
}

@Override
public Module loadModule(final Module caller, final String path, final JsonNode metadata) {
return null;
}

@Override
public JsonNode loadData(final Module caller, final String path, final JsonNode metadata) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
import net.thisptr.jackson.jq.path.Path;

public class ArrayConstruction implements Expression {

private Expression q;
public final Expression q;

public ArrayConstruction() {
this(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,61 @@
import net.thisptr.jackson.jq.Scope;
import net.thisptr.jackson.jq.Version;
import net.thisptr.jackson.jq.exception.JsonQueryException;
import net.thisptr.jackson.jq.module.Module;
import net.thisptr.jackson.jq.path.Path;

public class FunctionCall implements Expression {
private String name;
private List<Expression> args;
private Version version;
private final String name;
private final List<Expression> args;
private final Version version;
private final String moduleName;

public FunctionCall(final String name, final List<Expression> args, final Version version) {
public FunctionCall(final String moduleName, final String name, final List<Expression> args, final Version version) {
this.moduleName = moduleName;
this.name = name;
this.args = args;
this.version = version;
}

private Function lookupFunction(final Scope scope) throws JsonQueryException {
if (moduleName != null) {
for (final Module module : scope.getImportedModules(moduleName)) {
final Function f = module.getFunction(name, args.size());
if (f != null)
return f;
}
throw new JsonQueryException(String.format("Function %s::%s/%s does not exist", moduleName, name, args.size()));
} else {
final Function f = scope.getFunction(name, args.size());
if (f != null)
return f;

// search functions loaded by "include" statement
for (final Module module : scope.getImportedModules(null)) {
final Function g = module.getFunction(name, args.size());
if (g != null)
return g;
}

throw new JsonQueryException(String.format("Function %s/%s does not exist", name, args.size()));
}
}

@Override
public void apply(Scope scope, JsonNode in, Path path, PathOutput output, final boolean requirePath) throws JsonQueryException {
final Function f = scope.getFunction(name, args.size());
if (f == null)
throw new JsonQueryException(String.format("Function %s/%s does not exist", name, args.size()));
final Function f = lookupFunction(scope);
f.apply(scope, args, in, path, output, version);
}

@Override
public String toString() {
if (args.isEmpty()) {
return String.format("%s", name);
} else {
final StringBuilder builder = new StringBuilder(name);
final StringBuilder builder = new StringBuilder();
if (moduleName != null) {
builder.append(moduleName);
builder.append("::");
}
builder.append(name);
if (!args.isEmpty()) {
builder.append("(");
String sep = "";
for (final Expression arg : args) {
Expand All @@ -49,7 +77,7 @@ public String toString() {
sep = "; ";
}
builder.append(")");
return builder.toString();
}
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import net.thisptr.jackson.jq.internal.misc.JsonNodeUtils;

public class IdentifierKeyFieldConstruction implements FieldConstruction {
private final String key;
private final Expression value;
public final String key;
public final Expression value;

public IdentifierKeyFieldConstruction(final String key, final Expression value) {
this.key = key;
Expand Down
Loading

0 comments on commit d3a2b7e

Please sign in to comment.