From 750550e6df3cf8c9013f7fa455218b8d3847e181 Mon Sep 17 00:00:00 2001 From: "pabs@pablotron.org" Date: Thu, 18 Dec 2014 15:10:23 -0500 Subject: add java implementation --- java/Makefile | 14 +++ java/pablotron/luigi/Filter.java | 89 +++++++++++++++++++ java/pablotron/luigi/FilterReference.java | 11 +++ java/pablotron/luigi/LuigiError.java | 7 ++ java/pablotron/luigi/Parser.java | 114 +++++++++++++++++++++++++ java/pablotron/luigi/Template.java | 37 ++++++++ java/pablotron/luigi/actions/Action.java | 12 +++ java/pablotron/luigi/actions/FilterAction.java | 41 +++++++++ java/pablotron/luigi/actions/TextAction.java | 22 +++++ 9 files changed, 347 insertions(+) create mode 100644 java/Makefile create mode 100644 java/pablotron/luigi/Filter.java create mode 100644 java/pablotron/luigi/FilterReference.java create mode 100644 java/pablotron/luigi/LuigiError.java create mode 100644 java/pablotron/luigi/Parser.java create mode 100644 java/pablotron/luigi/Template.java create mode 100644 java/pablotron/luigi/actions/Action.java create mode 100644 java/pablotron/luigi/actions/FilterAction.java create mode 100644 java/pablotron/luigi/actions/TextAction.java diff --git a/java/Makefile b/java/Makefile new file mode 100644 index 0000000..15005fa --- /dev/null +++ b/java/Makefile @@ -0,0 +1,14 @@ +SOURCES=$(shell find . -type f -name \*.java) +CLASSES=$(shell find . -type f -name \*.class) +OUT=luigi-template.jar + +all: $(SOURCES) + javac $(SOURCES) + +$(OUT): all + jar cvf $(OUT) $(CLASSES) + +jar: $(OUT) + +clean: + rm -v $(OUT) $(CLASSES) diff --git a/java/pablotron/luigi/Filter.java b/java/pablotron/luigi/Filter.java new file mode 100644 index 0000000..d89beab --- /dev/null +++ b/java/pablotron/luigi/Filter.java @@ -0,0 +1,89 @@ +package 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 row); + }; + + public static Map FILTERS = new HashMap() {{ + put("null", new Handler() { + public String filter(String val, String args[], Map row) { + return ""; + } + }); + + put("s", new Handler() { + public String filter(String val, String args[], Map row) { + int v = Integer.parseInt(val); + return (v == 1) ? "" : "s"; + } + }); + + put("uc", new Handler() { + public String filter(String val, String args[], Map row) { + return val.toUpperCase(); + } + }); + + put("lc", new Handler() { + public String filter(String val, String args[], Map row) { + return val.toLowerCase(); + } + }); + + put("length", new Handler() { + public String filter(String val, String args[], Map row) { + return Integer.toString(val.length()); + } + }); + + put("trim", new Handler() { + public String filter(String val, String args[], Map row) { + return val.trim(); + } + }); + + put("h", new Handler() { + public String filter(String val, String args[], Map 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("&"); + break; + case '<': + r.append("<"); + break; + case '>': + r.append(">"); + break; + case '\'': + r.append("'"); + break; + case '"': + r.append("""); + break; + default: + r.append(c); + } + } + + return r.toString(); +/* + * return val + * .replace("&", "&") + * .replace("<", "<") + * .replace(">", ">") + * .replace("'", "'") + * .replace("\"", """); + */ + } + }); + }}; +}; diff --git a/java/pablotron/luigi/FilterReference.java b/java/pablotron/luigi/FilterReference.java new file mode 100644 index 0000000..d0f9d3d --- /dev/null +++ b/java/pablotron/luigi/FilterReference.java @@ -0,0 +1,11 @@ +package 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/pablotron/luigi/LuigiError.java b/java/pablotron/luigi/LuigiError.java new file mode 100644 index 0000000..4ff4c01 --- /dev/null +++ b/java/pablotron/luigi/LuigiError.java @@ -0,0 +1,7 @@ +package pablotron.luigi; + +public class LuigiError extends Exception { + public LuigiError(final String message) { + super(message); + } +}; diff --git a/java/pablotron/luigi/Parser.java b/java/pablotron/luigi/Parser.java new file mode 100644 index 0000000..0daef25 --- /dev/null +++ b/java/pablotron/luigi/Parser.java @@ -0,0 +1,114 @@ +package pablotron.luigi; + +import java.util.ArrayList; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +import pablotron.luigi.actions.Action; +import pablotron.luigi.actions.FilterAction; +import pablotron.luigi.actions.TextAction; +import pablotron.luigi.FilterReference; +import pablotron.luigi.LuigiError; + +public final class Parser { + private static final Pattern RE_ACTION = Pattern.compile( + // match opening brace + "%\\{" + + + // match optional whitespace + "\\s*" + + + // match key + // "(?[^\\s\\|\\}]+)" + + "([^\\s\\|\\}]+)" + + + // match filter(s) + // "(?(\\s*\\|(\\s*[^\\s\\|\\}]+)+)*)" + + "((\\s*\\|(\\s*[^\\s\\|\\}]+)+)*)" + + + // match optional whitespace + "\\s*" + + + // match closing brace + "\\}" + + + // or match up all non-% chars or a single % char + // "| (?[^%]* | %)", + "| ([^%]* | %)", + + Pattern.COMMENTS + ); + + private static final Pattern RE_FILTER = Pattern.compile( + // match filter name + // "(?\\S+)" + + "(\\S+)" + + + // match filter arguments (optional) + // "(?(\\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( + String template + ) throws LuigiError { + ArrayList r = new ArrayList(); + + // match on text + final Matcher m = RE_ACTION.matcher(template); + + while (m.find()) { + // String key = m.group("key"); + 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()]); + } + + public static FilterReference[] parse_filters( + String filters_str + ) throws LuigiError { + ArrayList r = new ArrayList(); + + // 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 + Matcher m = RE_FILTER.matcher(f); + if (!m.find()) + throw new LuigiError("invalid filter: " + f); + + // append new filter reference to result + r.add(new FilterReference(m.group(1), RE_DELIM_ARGS.split(m.group(2)))); + } + + // return result + return r.toArray(new FilterReference[r.size()]); + } +}; diff --git a/java/pablotron/luigi/Template.java b/java/pablotron/luigi/Template.java new file mode 100644 index 0000000..2bf0bb7 --- /dev/null +++ b/java/pablotron/luigi/Template.java @@ -0,0 +1,37 @@ +package pablotron.luigi; + +import java.util.Map; +import pablotron.luigi.Parser; +import pablotron.luigi.Filter; +import pablotron.luigi.LuigiError; +import 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 filters; + + public Template( + final String template, + final Map 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(Map args) throws LuigiError { + StringBuilder r = new StringBuilder(); + + for (Action a: this.actions) + r.append(a.run(this.filters, args)); + + return r.toString(); + } +}; diff --git a/java/pablotron/luigi/actions/Action.java b/java/pablotron/luigi/actions/Action.java new file mode 100644 index 0000000..010622a --- /dev/null +++ b/java/pablotron/luigi/actions/Action.java @@ -0,0 +1,12 @@ +package pablotron.luigi.actions; + +import java.util.Map; +import pablotron.luigi.Filter; +import pablotron.luigi.LuigiError; + +public interface Action { + public String run( + Map filters, + Map args + ) throws LuigiError; +}; diff --git a/java/pablotron/luigi/actions/FilterAction.java b/java/pablotron/luigi/actions/FilterAction.java new file mode 100644 index 0000000..b69923b --- /dev/null +++ b/java/pablotron/luigi/actions/FilterAction.java @@ -0,0 +1,41 @@ +package pablotron.luigi.actions; + +import java.util.Map; +import pablotron.luigi.actions.Action; +import pablotron.luigi.FilterReference; +import pablotron.luigi.Filter; +import 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 filters, + Map 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/pablotron/luigi/actions/TextAction.java b/java/pablotron/luigi/actions/TextAction.java new file mode 100644 index 0000000..3024f23 --- /dev/null +++ b/java/pablotron/luigi/actions/TextAction.java @@ -0,0 +1,22 @@ +package pablotron.luigi.actions; + +import java.util.Map; +import pablotron.luigi.actions.Action; +import pablotron.luigi.Filter; +import 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 filters, + Map args + ) throws LuigiError { + return this.text; + } +}; + -- cgit v1.2.3