diff --git a/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/BindType.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/BindType.java new file mode 100644 index 000000000..14211366e --- /dev/null +++ b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/BindType.java @@ -0,0 +1,30 @@ +package io.nosqlbench.engine.api.templating; + +/** + * The type of a parsed template depends on the structure of the bindings provided. + */ +public enum BindType { + + /** + * A literal template is one which has no bindings that need to be provided to render a specific statement. + * These templates are basically static statements. + * Example: {@code truncate testks.testtable;} + */ + literal, + + /** + * A bindref template is one which has only a single bind point and no leading or trailing text. + * It represents a single value which is to be injected, with no clear indication as to whether the + * value should be in string form or not. These are used when referencing objects by bind point name. + * Callers which use rawbind templates where Strings are needed should convert them with {@link Object#toString()}} + * Example: {@code {myvalue}} + */ + bindref, + + /** + * A string template is one which is neither a literal template nor a bindref template. This includes + * any template which has any amount of literal text and any template with more than one bind point. + */ + concat + +} diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/NamedTarget.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/NamedTarget.java similarity index 100% rename from adapters-api/src/main/java/io/nosqlbench/engine/api/templating/NamedTarget.java rename to virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/NamedTarget.java diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ObjectCache.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ObjectCache.java similarity index 100% rename from adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ObjectCache.java rename to virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ObjectCache.java diff --git a/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateList.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateList.java new file mode 100644 index 000000000..fe9838798 --- /dev/null +++ b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateList.java @@ -0,0 +1,51 @@ +package io.nosqlbench.engine.api.templating; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.LongFunction; + +public class ParsedTemplateList implements LongFunction> { + private final List protolist = new ArrayList<>(); + private final int[] dynamic_idx; + private final LongFunction[] functions; + + public ParsedTemplateList(List sublist, Map bindings, List> cfgsources) { + + List> funcs = new ArrayList<>(); + List dindexes = new ArrayList<>(); + + for (int i = 0; i < sublist.size(); i++) { + Object item = sublist.get(i); + Templatizer.Result result = Templatizer.make(bindings, item, null, cfgsources); + switch (result.getType()) { + case literal: + protolist.add(result.getValue()); + break; + case bindref: + case concat: + protolist.add(null); + funcs.add(result.getFunction()); + dindexes.add(i); + break; + } + } + this.dynamic_idx = dindexes.stream().mapToInt(Integer::intValue).toArray(); + this.functions = funcs.toArray(new LongFunction[0]); + + } + + @Override + public List apply(long value) { + List list = new ArrayList<>(protolist); + for (int i = 0; i < dynamic_idx.length; i++) { + Object obj = functions[i].apply(value); + list.set(dynamic_idx[i], obj); + } + return list; + } + + public boolean isStatic() { + return dynamic_idx.length==0; + } +} diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateMap.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateMap.java similarity index 99% rename from adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateMap.java rename to virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateMap.java index 3ec03fb1a..82c19a889 100644 --- a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateMap.java +++ b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/ParsedTemplateMap.java @@ -118,7 +118,9 @@ public class ParsedTemplateMap implements LongFunction>, StaticFi dynamics.put(k, subtpl); protomap.put(k, null); } - + } else if (v instanceof List) { + List sublist = (List) v; + ParsedTemplateList subtpl = new ParsedTemplateList(sublist, bindings, cfgsources); } else { // Eventually, nested and mixed static dynamic structure could be supported, but // it would be complex to implement and also not that efficient, so let's just copy diff --git a/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/Templatizer.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/Templatizer.java new file mode 100644 index 000000000..60fd3ef91 --- /dev/null +++ b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/Templatizer.java @@ -0,0 +1,126 @@ +package io.nosqlbench.engine.api.templating; + +import io.nosqlbench.nb.api.errors.OpConfigError; +import io.nosqlbench.virtdata.core.bindings.DataMapper; +import io.nosqlbench.virtdata.core.bindings.VirtData; +import io.nosqlbench.virtdata.core.templates.CapturePoint; +import io.nosqlbench.virtdata.core.templates.ParsedTemplate; +import io.nosqlbench.virtdata.core.templates.StringBindings; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.LongFunction; + +public class Templatizer { + + public static Result make(Map bindings, Object v, String name, List> cfgsources) { + Result result = new Result(); + result.setName(name); + + if (v instanceof CharSequence) { + ParsedTemplate pt = ParsedTemplate.of(((CharSequence) v).toString(), bindings); + result.addCaptures(pt.getCaptures()); + result.setType(pt.getType()); + switch (pt.getType()) { + case literal: + result.setValue(((CharSequence)v).toString()); + break; + case bindref: + String spec = pt.asBinding().orElseThrow().getBindspec(); + if (spec == null) { + throw new OpConfigError("Empty binding spec for '" + (name!=null?name:"anonymous binding") + "'"); + } + Optional> mapper = VirtData.getOptionalMapper(spec); + result.setFunction(mapper.orElseThrow()); + break; + case concat: + StringBindings sb = new StringBindings(pt); + result.setFunction(sb); + break; + } + } else if (v instanceof Map) { + ((Map) v).keySet().forEach(smk -> { + if (!CharSequence.class.isAssignableFrom(smk.getClass())) { + throw new OpConfigError("Only string keys are allowed in submaps."); + } + }); + Map submap = (Map) v; + ParsedTemplateMap subtpl = new ParsedTemplateMap(submap, bindings, cfgsources); + if (subtpl.isStatic()) { + result.setValue(submap); + } else { + result.setFunction(subtpl); + } + } else if (v instanceof List) { + List sublist = (List) v; + ParsedTemplateList subtpl = new ParsedTemplateList(sublist, bindings, cfgsources); + if (subtpl.isStatic()) { + result.setValue(sublist); + } else { + result.setFunction(subtpl); + } + } else { + result.setValue(v); + // Eventually, nested and mixed static dynamic structure could be supported, but + // it would be complex to implement and also not that efficient, so let's just copy + // structure for now + } + return result; + } + + public static class Result { + private BindType type; + private final List captures = new ArrayList<>(); + private String name; + private Object value; + private LongFunction function; + + public Result() { + this.type = null; + } + + public BindType getType() { + return this.type; + } + + public List getCaptures() { + return this.captures; + } + + public void addCaptures(List captures) { + this.captures.addAll(captures); + } + + public void setType(BindType type) { + this.type = type; + } + + public void setName(String name) { + this.name =name; + } + + public String getName() { + return name; + } + + public void setValue(Object v) { + this.value = v; + } + + public void setFunction(LongFunction mapper) { + this.function = mapper; + } + + public Object getValue() { + return value; + } + + public LongFunction getFunction() { + return this.function; + } + } + + +} diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ArrayBinder.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ArrayBinder.java similarity index 100% rename from adapters-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ArrayBinder.java rename to virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ArrayBinder.java diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ListBinder.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ListBinder.java similarity index 100% rename from adapters-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ListBinder.java rename to virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/binders/ListBinder.java diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/templating/binders/OrderedMapBinder.java b/virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/binders/OrderedMapBinder.java similarity index 100% rename from adapters-api/src/main/java/io/nosqlbench/engine/api/templating/binders/OrderedMapBinder.java rename to virtdata-api/src/main/java/io/nosqlbench/engine/api/templating/binders/OrderedMapBinder.java diff --git a/virtdata-api/src/main/java/io/nosqlbench/virtdata/core/templates/ParsedTemplate.java b/virtdata-api/src/main/java/io/nosqlbench/virtdata/core/templates/ParsedTemplate.java index 88e8d52ba..746430a48 100644 --- a/virtdata-api/src/main/java/io/nosqlbench/virtdata/core/templates/ParsedTemplate.java +++ b/virtdata-api/src/main/java/io/nosqlbench/virtdata/core/templates/ParsedTemplate.java @@ -18,6 +18,7 @@ package io.nosqlbench.virtdata.core.templates; +import io.nosqlbench.engine.api.templating.BindType; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -118,32 +119,6 @@ public class ParsedTemplate { return new ParsedTemplate(rawtemplate, bindings); } - /** - * The type of a parsed template depends on the structure of the bindings provided. - */ - public enum Type { - - /** - * A literal template is one which has no bindings that need to be provided to render a specific statement. - * These templates are basically static statements. - * Example: {@code truncate testks.testtable;} - */ - literal, - /** - * A bindref template is one which has only a single bind point and no leading or trailing text. - * It represents a single value which is to be injected, with no clear indication as to whether the - * value should be in string form or not. These are used when referencing objects by bind point name. - * Callers which use rawbind templates where Strings are needed should convert them with {@link Object#toString()}} - * Example: {@code {myvalue}} - */ - bindref, - /** - * A string template is one which is neither a literal template nor a bindref template. This includes - * any template which has any amount of literal text and any template with more than one bind point. - */ - concat - } - /** * Spans is an even-odd form of (literal, variable, ..., ..., literal) * Thus a 1-length span is a single literal, and a 3 length span has a single bind point @@ -176,13 +151,13 @@ public class ParsedTemplate { this.bindpoints = bindPointsResult.getBindpoints(); } - public Type getType() { + public BindType getType() { if (this.spans.length == 1) { - return Type.literal; + return BindType.literal; } else if (this.spans[0].isEmpty() && this.spans[2].isEmpty()) { - return Type.bindref; + return BindType.bindref; } else { - return Type.concat; + return BindType.concat; } } diff --git a/virtdata-api/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/virtdata-api/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000..ceb8ce941 --- /dev/null +++ b/virtdata-api/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +io.nosqlbench.nb.annotations.ServiceProcessor diff --git a/virtdata-api/src/test/java/io/nosqlbench/engine/api/templating/ParsedTemplateListTest.java b/virtdata-api/src/test/java/io/nosqlbench/engine/api/templating/ParsedTemplateListTest.java new file mode 100644 index 000000000..73e8fca42 --- /dev/null +++ b/virtdata-api/src/test/java/io/nosqlbench/engine/api/templating/ParsedTemplateListTest.java @@ -0,0 +1,26 @@ +package io.nosqlbench.engine.api.templating; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ParsedTemplateListTest { + + @Test + public void testTemplateListLiterals() { + ParsedTemplateList ptl = new ParsedTemplateList(List.of("a","b"), Map.of(),List.of()); + List made = ptl.apply(2L); + assertThat(made).isEqualTo(List.of("a","b")); + } + +// @Test +// public void testTemplateListReferences() { +// ParsedTemplateList ptl = new ParsedTemplateList(List.of("{a}","{b}"), Map.of("a","TestingRepeater(2)","b","TestingRepeater(4)"),List.of()); +// List made = ptl.apply(2L); +// assertThat(made).isEqualTo(List.of("a","b")); +// } + +} diff --git a/virtdata-api/src/test/java/io/nosqlbench/virtdata/core/templates/ParsedTemplateTest.java b/virtdata-api/src/test/java/io/nosqlbench/virtdata/core/templates/ParsedTemplateTest.java index 789df5534..953d851c6 100644 --- a/virtdata-api/src/test/java/io/nosqlbench/virtdata/core/templates/ParsedTemplateTest.java +++ b/virtdata-api/src/test/java/io/nosqlbench/virtdata/core/templates/ParsedTemplateTest.java @@ -1,12 +1,9 @@ package io.nosqlbench.virtdata.core.templates; -import org.junit.jupiter.api.Disabled; +import io.nosqlbench.engine.api.templating.BindType; import org.junit.jupiter.api.Test; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import java.util.Map; -import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -82,7 +79,7 @@ public class ParsedTemplateTest { "select [u],[v as v1] from users where userid={userid}", Map.of("userid", "NumberNameToString()") ); assertThat(pt.getAnchors()).containsExactly("userid"); - assertThat(pt.getType()).isEqualTo(ParsedTemplate.Type.concat); + assertThat(pt.getType()).isEqualTo(BindType.concat); assertThat(pt.getCaptures()).containsExactly(CapturePoint.of("u"),CapturePoint.of("v","v1")); }