From 3852d967f301ddd2cbd1179894bdc83a673848ac Mon Sep 17 00:00:00 2001 From: Lars Marius Garshol Date: Thu, 14 Jun 2018 15:11:00 +0200 Subject: [PATCH] Version 1.0.1: implement resource resolution --- README.md | 3 +- build.gradle | 2 +- .../com/schibsted/spt/data/jslt/Parser.java | 21 ++++++++--- .../spt/data/jslt/ResourceResolver.java | 32 ++++++++++++++++ .../jslt/impl/ClasspathResourceResolver.java | 37 +++++++++++++++++++ .../spt/data/jslt/impl/ParseContext.java | 14 +++++-- .../spt/data/jslt/parser/ParserImpl.java | 12 ++---- 7 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/schibsted/spt/data/jslt/ResourceResolver.java create mode 100644 src/main/java/com/schibsted/spt/data/jslt/impl/ClasspathResourceResolver.java diff --git a/README.md b/README.md index 26b0e3e4..d1bfd453 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ To include JSLT in your project, depend on: com.schibsted.spt.data jslt - 0.1.0 + 0.1.1 ``` @@ -114,7 +114,6 @@ Unless required by applicable law or agreed to in writing, software distributed ## What is missing Things to be done: - * Add API for pluggable import resolution. * Move the tests out into JSON files. * Write a proper spec with EBNF and everything. * Fix the syntax ambiguity problem with `let` and `def`. diff --git a/build.gradle b/build.gradle index b1fefb41..81079d7f 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ ext { } group 'com.schibsted.spt.data' -version "0.1.0" +version "0.1.1" project.description "A JSON query and transformation language" check { diff --git a/src/main/java/com/schibsted/spt/data/jslt/Parser.java b/src/main/java/com/schibsted/spt/data/jslt/Parser.java index 36fed5dc..7dcb8d27 100644 --- a/src/main/java/com/schibsted/spt/data/jslt/Parser.java +++ b/src/main/java/com/schibsted/spt/data/jslt/Parser.java @@ -130,18 +130,22 @@ public static Expression compile(String source, private Collection functions; private String source; private Reader reader; + private ResourceResolver resolver; - private Parser(String source, Reader reader, Collection functions) { + private Parser(String source, Reader reader, Collection functions, + ResourceResolver resolver) { this.functions = functions; this.source = source; this.reader = reader; + this.resolver = resolver; } /** * Create a Parser reading JSLT source from the given Reader. */ public Parser(Reader reader) { - this("", reader, Collections.EMPTY_SET); + this("", reader, Collections.EMPTY_SET, + new ClasspathResourceResolver()); } /** @@ -149,21 +153,28 @@ public Parser(Reader reader) { * used in error messages. */ public Parser withSource(String thisSource) { - return new Parser(thisSource, reader, functions); + return new Parser(thisSource, reader, functions, resolver); } /** * Create a new Parser with the given extension functions. */ public Parser withFunctions(Collection theseFunctions) { - return new Parser(source, reader, theseFunctions); + return new Parser(source, reader, theseFunctions, resolver); + } + + /** + * Create a new Parser with the given resource resolver. + */ + public Parser withResourceResolver(ResourceResolver thisResolver) { + return new Parser(source, reader, functions, thisResolver); } /** * Compile the JSLT from the defined parameters. */ public Expression compile() { - ParseContext ctx = new ParseContext(functions, source); + ParseContext ctx = new ParseContext(functions, source, resolver); return ParserImpl.compileExpression(ctx, new JsltParser(reader)); } } diff --git a/src/main/java/com/schibsted/spt/data/jslt/ResourceResolver.java b/src/main/java/com/schibsted/spt/data/jslt/ResourceResolver.java new file mode 100644 index 00000000..571676b8 --- /dev/null +++ b/src/main/java/com/schibsted/spt/data/jslt/ResourceResolver.java @@ -0,0 +1,32 @@ + +// Copyright 2018 Schibsted Marketplaces Products & Technology As +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.schibsted.spt.data.jslt; + +import java.io.Reader; + +/** + * Given a string identifying a JSLT module file, return a Reader that + * produces the module. This interface can be used to look up module + * files other places than just on the classpath. + */ +public interface ResourceResolver { + + /** + * Return a Reader for the given module. + */ + public Reader resolve(String jslt); + +} diff --git a/src/main/java/com/schibsted/spt/data/jslt/impl/ClasspathResourceResolver.java b/src/main/java/com/schibsted/spt/data/jslt/impl/ClasspathResourceResolver.java new file mode 100644 index 00000000..5b6cb075 --- /dev/null +++ b/src/main/java/com/schibsted/spt/data/jslt/impl/ClasspathResourceResolver.java @@ -0,0 +1,37 @@ + +// Copyright 2018 Schibsted Marketplaces Products & Technology As +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.schibsted.spt.data.jslt.impl; + +import java.io.Reader; +import java.io.InputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import com.schibsted.spt.data.jslt.JsltException; +import com.schibsted.spt.data.jslt.ResourceResolver; + +public class ClasspathResourceResolver implements ResourceResolver { + + public Reader resolve(String jslt) { + try { + InputStream is = getClass().getClassLoader().getResourceAsStream(jslt); + if (is == null) + throw new JsltException("Cannot load resource '" + jslt + "': not found"); + return new InputStreamReader(is, "utf-8"); + } catch (IOException e) { + throw new JsltException("Couldn't load resource '" + jslt + "': " + e, e); + } + } +} diff --git a/src/main/java/com/schibsted/spt/data/jslt/impl/ParseContext.java b/src/main/java/com/schibsted/spt/data/jslt/impl/ParseContext.java index 9caf5c25..ba5bce55 100644 --- a/src/main/java/com/schibsted/spt/data/jslt/impl/ParseContext.java +++ b/src/main/java/com/schibsted/spt/data/jslt/impl/ParseContext.java @@ -20,8 +20,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import com.schibsted.spt.data.jslt.JsltException; import com.schibsted.spt.data.jslt.Function; +import com.schibsted.spt.data.jslt.JsltException; +import com.schibsted.spt.data.jslt.ResourceResolver; /** * Class to encapsulate context information like available functions, @@ -41,8 +42,10 @@ public class ParseContext { private Map modules; private Collection funcalls; // delayed function resolution private ParseContext parent; + private ResourceResolver resolver; - public ParseContext(Collection extensions, String source) { + public ParseContext(Collection extensions, String source, + ResourceResolver resolver) { this.extensions = extensions; this.functions = new HashMap(); for (Function func : extensions) @@ -51,10 +54,11 @@ public ParseContext(Collection extensions, String source) { this.source = source; this.funcalls = new ArrayList(); this.modules = new HashMap(); + this.resolver = resolver; } public ParseContext(String source) { - this(Collections.EMPTY_SET, source); + this(Collections.EMPTY_SET, source, new ClasspathResourceResolver()); } public void setParent(ParseContext parent) { @@ -127,4 +131,8 @@ public Function getImportedFunction(String prefix, String name, Location loc) { return f; } + + public ResourceResolver getResolver() { + return resolver; + } } diff --git a/src/main/java/com/schibsted/spt/data/jslt/parser/ParserImpl.java b/src/main/java/com/schibsted/spt/data/jslt/parser/ParserImpl.java index 1c86b35e..71cd2256 100644 --- a/src/main/java/com/schibsted/spt/data/jslt/parser/ParserImpl.java +++ b/src/main/java/com/schibsted/spt/data/jslt/parser/ParserImpl.java @@ -57,17 +57,13 @@ public static Expression compileExpression(ParseContext ctx, JsltParser parser) private static ExpressionImpl compileImport(Collection functions, ParseContext parent, - String jstl) { - try (InputStream stream = ParserImpl.class.getClassLoader().getResourceAsStream(jstl)) { - if (stream == null) - throw new JsltException("Cannot load resource '" + jstl + "': not found"); - - Reader reader = new InputStreamReader(stream, "UTF-8"); - ParseContext ctx = new ParseContext(functions, jstl); + String jslt) { + try (Reader reader = parent.getResolver().resolve(jslt)) { + ParseContext ctx = new ParseContext(functions, jslt, parent.getResolver()); ctx.setParent(parent); return compileModule(ctx, new JsltParser(reader)); } catch (IOException e) { - throw new JsltException("Couldn't read resource " + jstl, e); + throw new JsltException("Couldn't read resource " + jslt, e); } }