summaryrefslogtreecommitdiff
path: root/java/src
diff options
context:
space:
mode:
authorPaul Duncan <pabs@pablotron.org>2018-09-06 08:40:23 -0400
committerPaul Duncan <pabs@pablotron.org>2018-09-06 08:40:23 -0400
commitf422534cd4cf1ba32523c9fc798c207065038ca5 (patch)
treed3dce87dffeca2acfceab6aa9abf7046843f9145 /java/src
parentc02640d395fe95a41183a63bea29113676e92fae (diff)
downloadluigi-template-f422534cd4cf1ba32523c9fc798c207065038ca5.tar.bz2
luigi-template-f422534cd4cf1ba32523c9fc798c207065038ca5.zip
java: add pom.xml, mv sources to src/main/java/org/pablotron, add src/test, add initial junit tests, update makefile and .gitignore
Diffstat (limited to 'java/src')
-rw-r--r--java/src/main/java/org/pablotron/luigi/Cache.java51
-rw-r--r--java/src/main/java/org/pablotron/luigi/Filter.java89
-rw-r--r--java/src/main/java/org/pablotron/luigi/FilterReference.java11
-rw-r--r--java/src/main/java/org/pablotron/luigi/LuigiError.java7
-rw-r--r--java/src/main/java/org/pablotron/luigi/Parser.java122
-rw-r--r--java/src/main/java/org/pablotron/luigi/Template.java41
-rw-r--r--java/src/main/java/org/pablotron/luigi/Test.java57
-rw-r--r--java/src/main/java/org/pablotron/luigi/actions/Action.java12
-rw-r--r--java/src/main/java/org/pablotron/luigi/actions/FilterAction.java41
-rw-r--r--java/src/main/java/org/pablotron/luigi/actions/TextAction.java22
-rw-r--r--java/src/test/java/org/pablotron/luigi/tests/TemplateTest.java69
11 files changed, 522 insertions, 0 deletions
diff --git a/java/src/main/java/org/pablotron/luigi/Cache.java b/java/src/main/java/org/pablotron/luigi/Cache.java
new file mode 100644
index 0000000..97fa0ab
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/Cache.java
@@ -0,0 +1,51 @@
+package org.pablotron.luigi;
+
+import java.util.Map;
+import java.util.HashMap;
+import org.pablotron.luigi.Filter;
+import org.pablotron.luigi.Template;
+import org.pablotron.luigi.LuigiError;
+import org.pablotron.luigi.actions.Action;
+
+public final class Cache {
+ private final Map<String, String> strings;
+ private final Map<String, Filter.Handler> filters;
+ private final Map<String, Template> templates = new HashMap<String, Template>();
+
+ public Cache(
+ final Map<String, String> strings,
+ final Map<String, Filter.Handler> filters
+ ) {
+ this.strings = strings;
+ this.filters = filters;
+ }
+
+ public Cache(final Map<String, String> strings) {
+ this(strings, Filter.FILTERS);
+ }
+
+ public String run(
+ final String key,
+ final Map<String, String> args
+ ) throws LuigiError {
+ Template t;
+
+ if (templates.containsKey(key)) {
+ // get template
+ t = templates.get(key);
+ } else {
+ // make sure template exists
+ if (!strings.containsKey(key))
+ throw new LuigiError("unknown template: " + key);
+
+ // create template
+ t = new Template(strings.get(key), filters);
+
+ // cache template
+ templates.put(key, t);
+ }
+
+ // run template with args
+ return t.run(args);
+ }
+};
diff --git a/java/src/main/java/org/pablotron/luigi/Filter.java b/java/src/main/java/org/pablotron/luigi/Filter.java
new file mode 100644
index 0000000..b00491f
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/Filter.java
@@ -0,0 +1,89 @@
+package org.pablotron.luigi;
+
+import java.util.Map;
+import java.util.HashMap;
+
+public final class Filter {
+ public interface Handler {
+ public String filter(String val, String args[], Map<String, String> row);
+ };
+
+ public static Map<String, Handler> FILTERS = new HashMap<String, Handler>() {{
+ put("null", new Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ return "";
+ }
+ });
+
+ put("s", new Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ int v = Integer.parseInt(val);
+ return (v == 1) ? "" : "s";
+ }
+ });
+
+ put("uc", new Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ return val.toUpperCase();
+ }
+ });
+
+ put("lc", new Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ return val.toLowerCase();
+ }
+ });
+
+ put("length", new Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ return Integer.toString(val.length());
+ }
+ });
+
+ put("trim", new Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ return val.trim();
+ }
+ });
+
+ put("h", new Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ StringBuilder r = new StringBuilder(val.length());
+
+ for (int i = 0, l = val.length(); i < l; i++) {
+ char c = val.charAt(i);
+
+ switch (c) {
+ case '&':
+ r.append("&amp;");
+ break;
+ case '<':
+ r.append("&lt;");
+ break;
+ case '>':
+ r.append("&gt;");
+ break;
+ case '\'':
+ r.append("&apos;");
+ break;
+ case '"':
+ r.append("&quot;");
+ break;
+ default:
+ r.append(c);
+ }
+ }
+
+ return r.toString();
+/*
+ * return val
+ * .replace("&", "&amp;")
+ * .replace("<", "&lt;")
+ * .replace(">", "&gt;")
+ * .replace("'", "&apos;")
+ * .replace("\"", "&quot;");
+ */
+ }
+ });
+ }};
+};
diff --git a/java/src/main/java/org/pablotron/luigi/FilterReference.java b/java/src/main/java/org/pablotron/luigi/FilterReference.java
new file mode 100644
index 0000000..38c134b
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/FilterReference.java
@@ -0,0 +1,11 @@
+package org.pablotron.luigi;
+
+public final class FilterReference {
+ public final String name;
+ public final String[] args;
+
+ public FilterReference(final String name, final String args[]) {
+ this.name = name;
+ this.args = args;
+ }
+};
diff --git a/java/src/main/java/org/pablotron/luigi/LuigiError.java b/java/src/main/java/org/pablotron/luigi/LuigiError.java
new file mode 100644
index 0000000..edeb7c7
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/LuigiError.java
@@ -0,0 +1,7 @@
+package org.pablotron.luigi;
+
+public class LuigiError extends Exception {
+ public LuigiError(final String message) {
+ super(message);
+ }
+};
diff --git a/java/src/main/java/org/pablotron/luigi/Parser.java b/java/src/main/java/org/pablotron/luigi/Parser.java
new file mode 100644
index 0000000..5be4a9c
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/Parser.java
@@ -0,0 +1,122 @@
+package org.pablotron.luigi;
+
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import org.pablotron.luigi.actions.Action;
+import org.pablotron.luigi.actions.FilterAction;
+import org.pablotron.luigi.actions.TextAction;
+import org.pablotron.luigi.FilterReference;
+import org.pablotron.luigi.LuigiError;
+
+public final class Parser {
+ private static final Pattern RE_ACTION = Pattern.compile(
+ // match opening brace
+ "%\\{" +
+
+ // match optional whitespace
+ "\\s*" +
+
+ // match key
+ // "(?<key>[^\\s\\|\\}]+)" +
+ "([^\\s\\|\\}]+)" +
+
+ // match filter(s)
+ // "(?<filters>(\\s*\\|(\\s*[^\\s\\|\\}]+)+)*)" +
+ "((\\s*\\|(\\s*[^\\s\\|\\}]+)+)*)" +
+
+ // match optional whitespace
+ "\\s*" +
+
+ // match closing brace
+ "\\}" +
+
+ // or match up all non-% chars or a single % char
+ // "| (?<text>[^%]* | %)",
+ "| ([^%]* | %)",
+
+ Pattern.COMMENTS
+ );
+
+ private static final Pattern RE_FILTER = Pattern.compile(
+ // match filter name
+ // "(?<name>\\S+)" +
+ "(\\S+)" +
+
+ // match filter arguments (optional)
+ // "(?<args>(\\s*\\S+)*)" +
+ "((\\s*\\S+)*)" +
+
+ // optional trailing whitespace
+ "\\s*",
+
+ Pattern.COMMENTS
+ );
+
+ private static final Pattern RE_DELIM_FILTERS = Pattern.compile(
+ "\\s*\\|\\s*"
+ );
+
+ private static final Pattern RE_DELIM_ARGS = Pattern.compile(
+ "\\s+"
+ );
+
+ public static Action[] parse_template(
+ final String template
+ ) throws LuigiError {
+ final ArrayList<Action> r = new ArrayList<Action>();
+
+ // match on text
+ final Matcher m = RE_ACTION.matcher(template);
+
+ while (m.find()) {
+ // String key = m.group("key");
+ final String key = m.group(1);
+
+ if (key != null && key.length() > 0) {
+ // r.add(new FilterAction(key, parse_filters(m.group("filters"))));
+ r.add(new FilterAction(key, parse_filters(m.group(2))));
+ } else {
+ // r.add(new TextAction(m.group("text")));
+ r.add(new TextAction(m.group(5)));
+ }
+ }
+
+ // build array of results
+ return r.toArray(new Action[r.size()]);
+ }
+
+ private static final String[] NO_ARGS = {};
+
+ public static FilterReference[] parse_filters(
+ final String filters_str
+ ) throws LuigiError {
+ final ArrayList<FilterReference> r = new ArrayList<FilterReference>();
+
+ // split string into individual filters and handle each one
+ for (String f: RE_DELIM_FILTERS.split(filters_str)) {
+ // trim filter string and skip empty filters
+ f = f.trim();
+ if (f.length() == 0)
+ continue;
+
+ // match on filter and check for error
+ final Matcher m = RE_FILTER.matcher(f);
+ if (!m.find())
+ throw new LuigiError("invalid filter: " + f);
+
+ // get arguments string
+ final String args = m.group(2).trim();
+
+ // append new filter reference to result
+ r.add(new FilterReference(
+ m.group(1),
+ (args.length() > 0) ? RE_DELIM_ARGS.split(args) : NO_ARGS
+ ));
+ }
+
+ // return result
+ return r.toArray(new FilterReference[r.size()]);
+ }
+};
diff --git a/java/src/main/java/org/pablotron/luigi/Template.java b/java/src/main/java/org/pablotron/luigi/Template.java
new file mode 100644
index 0000000..2b17a54
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/Template.java
@@ -0,0 +1,41 @@
+package org.pablotron.luigi;
+
+import java.util.Map;
+import org.pablotron.luigi.Parser;
+import org.pablotron.luigi.Filter;
+import org.pablotron.luigi.LuigiError;
+import org.pablotron.luigi.actions.Action;
+
+public final class Template {
+ private static final String VERSION = "0.4.0";
+
+ private final String template;
+ private final Action actions[];
+ private final Map<String, Filter.Handler> filters;
+
+ public Template(
+ final String template,
+ final Map<String, Filter.Handler> filters
+ ) throws LuigiError {
+ this.template = template;
+ this.filters = filters;
+ this.actions = Parser.parse_template(template);
+ }
+
+ public Template(final String template) throws LuigiError {
+ this(template, Filter.FILTERS);
+ }
+
+ public String run(final Map<String, String> args) throws LuigiError {
+ final StringBuilder r = new StringBuilder();
+
+ for (Action a: this.actions)
+ r.append(a.run(this.filters, args));
+
+ return r.toString();
+ }
+
+ public String toString() {
+ return this.template;
+ }
+};
diff --git a/java/src/main/java/org/pablotron/luigi/Test.java b/java/src/main/java/org/pablotron/luigi/Test.java
new file mode 100644
index 0000000..2a560e2
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/Test.java
@@ -0,0 +1,57 @@
+package org.pablotron.luigi;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import org.pablotron.luigi.LuigiError;
+import org.pablotron.luigi.Filter;
+import org.pablotron.luigi.Template;
+import org.pablotron.luigi.Cache;
+
+public final class Test {
+ // test template
+ private static final String TEMPLATE =
+ "test basic: hello %{name}\n" +
+ "test filter: hello %{name | uc}\n" +
+ "test custom: %{name | custom | uc | lc}\n" +
+ "test custom_with_arg: %{name | custom_with_arg hello}\n";
+
+ // test template cache
+ private static final Cache cache = new Cache(new HashMap<String, String>() {{
+ put("test-template", TEMPLATE);
+ }});
+
+ // test arguments
+ private static final Map<String, String> args = new HashMap<String, String>() {{
+ put("name", "paul");
+ }};
+
+ // custom filters
+ private static final Map<String, Filter.Handler> filters = new HashMap<String, Filter.Handler>() {{
+ // add custom filter
+ put("custom", new Filter.Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ return "custom";
+ }
+ });
+
+ // add custom filter with argument
+ put("custom_with_arg", new Filter.Handler() {
+ public String filter(String val, String args[], Map<String, String> row) {
+ return (args.length > 0) ? args[0] : "custom";
+ }
+ });
+ }};
+
+ public static void main(String params[]) throws Exception {
+ // add custom filters
+ Filter.FILTERS.putAll(filters);
+
+ // create and run template
+ final Template t = new Template(TEMPLATE);
+ System.out.print(t.run(args));
+
+ // run cache
+ System.out.print(cache.run("test-template", args));
+ }
+};
diff --git a/java/src/main/java/org/pablotron/luigi/actions/Action.java b/java/src/main/java/org/pablotron/luigi/actions/Action.java
new file mode 100644
index 0000000..9708199
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/actions/Action.java
@@ -0,0 +1,12 @@
+package org.pablotron.luigi.actions;
+
+import java.util.Map;
+import org.pablotron.luigi.Filter;
+import org.pablotron.luigi.LuigiError;
+
+public interface Action {
+ public String run(
+ Map<String, Filter.Handler> filters,
+ Map<String, String> args
+ ) throws LuigiError;
+};
diff --git a/java/src/main/java/org/pablotron/luigi/actions/FilterAction.java b/java/src/main/java/org/pablotron/luigi/actions/FilterAction.java
new file mode 100644
index 0000000..9115a68
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/actions/FilterAction.java
@@ -0,0 +1,41 @@
+package org.pablotron.luigi.actions;
+
+import java.util.Map;
+import org.pablotron.luigi.actions.Action;
+import org.pablotron.luigi.FilterReference;
+import org.pablotron.luigi.Filter;
+import org.pablotron.luigi.LuigiError;
+
+public final class FilterAction implements Action {
+ private final String key;
+ private final FilterReference filters[];
+
+ public FilterAction(final String key, final FilterReference filters[]) {
+ this.key = key;
+ this.filters = filters;
+ }
+
+ public String run(
+ Map<String, Filter.Handler> filters,
+ Map<String, String> args
+ ) throws LuigiError {
+ // check for key
+ if (!args.containsKey(key))
+ throw new LuigiError("unknown key: " + key);
+
+ // reduce value to result
+ String r = args.get(key);
+ for (int i = 0, l = this.filters.length; i < l; i++) {
+ // get/check filter
+ Filter.Handler f = filters.get(this.filters[i].name);
+ if (f == null)
+ throw new LuigiError("unknown filter: " + this.filters[i].name);
+
+ // run filter
+ r = f.filter(r, this.filters[i].args, args);
+ }
+
+ // return result
+ return r;
+ }
+};
diff --git a/java/src/main/java/org/pablotron/luigi/actions/TextAction.java b/java/src/main/java/org/pablotron/luigi/actions/TextAction.java
new file mode 100644
index 0000000..0ad4382
--- /dev/null
+++ b/java/src/main/java/org/pablotron/luigi/actions/TextAction.java
@@ -0,0 +1,22 @@
+package org.pablotron.luigi.actions;
+
+import java.util.Map;
+import org.pablotron.luigi.actions.Action;
+import org.pablotron.luigi.Filter;
+import org.pablotron.luigi.LuigiError;
+
+public final class TextAction implements Action {
+ private final String text;
+
+ public TextAction(final String text) {
+ this.text = text;
+ }
+
+ public String run(
+ Map<String, Filter.Handler> filters,
+ Map<String, String> args
+ ) throws LuigiError {
+ return this.text;
+ }
+};
+
diff --git a/java/src/test/java/org/pablotron/luigi/tests/TemplateTest.java b/java/src/test/java/org/pablotron/luigi/tests/TemplateTest.java
new file mode 100644
index 0000000..5ff9ede
--- /dev/null
+++ b/java/src/test/java/org/pablotron/luigi/tests/TemplateTest.java
@@ -0,0 +1,69 @@
+import java.util.Map;
+import java.util.HashMap;
+
+import org.pablotron.luigi.LuigiError;
+import org.pablotron.luigi.Template;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import org.junit.jupiter.api.Test;
+
+
+public final class TemplateTest {
+ private static final Map<String, String> TEST_ARGS = new HashMap<String, String>() {{
+ put("bar", "foo");
+ }};
+
+ private static final Map<String, String> TEST_MULTIPLE_ARGS = new HashMap<String, String>() {{
+ put("bar", "foo");
+ put("baz", "bar");
+ }};
+
+ @Test
+ public void testNew() throws LuigiError {
+ final Template t = new Template("");
+
+ assertNotNull(t);
+ }
+
+ @Test
+ public void testRun() throws LuigiError {
+ final Template t = new Template("foo%{bar}");
+ final String r = t.run(TEST_ARGS);
+
+ assertEquals("foofoo", r);
+ }
+
+ @Test
+ public void testMultipleKeys() throws LuigiError {
+ final Template t = new Template("foo%{bar}%{baz}");
+ final String r = t.run(TEST_MULTIPLE_ARGS);
+
+ assertEquals("foofoobar", r);
+ }
+
+ @Test
+ public void testWhitespace() throws LuigiError {
+ final Template t = new Template("%{ bar}%{ bar }%{bar }");
+ final String r = t.run(TEST_ARGS);
+
+ assertEquals("foofoofoo", r);
+ }
+
+ @Test
+ public void testNewlines() throws LuigiError {
+ final Template t = new Template("%{\nbar}%{\n bar\n }%{bar\n}");
+ final String r = t.run(TEST_ARGS);
+
+ assertEquals("foofoofoo", r);
+ }
+
+ @Test
+ public void testToString() throws LuigiError {
+ final Template t = new Template("foo%{bar}");
+ final String r = t.toString();
+
+ assertEquals("foo%{bar}", r);
+ }
+};