Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into feat-perf
Browse files Browse the repository at this point in the history
  • Loading branch information
erdos committed Dec 10, 2022
2 parents 88a9eab + 27335c3 commit be9f9dd
Show file tree
Hide file tree
Showing 69 changed files with 1,098 additions and 723 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ junit.xml
/stencil-native
/*.docx
.DS_Store
.lsp/
.lsp/
*.jfr
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ The project has a simple [service implementation](https://github.com/erdos/stenc

## Version

**Latest stable** version is `0.3.31`
**Latest stable** version is `0.5.0`

**Latest snapshot** version is `0.3.32-SNAPSHOT`
**Latest snapshot** version is `0.5.1-SNAPSHOT`

If you are using Maven, add the followings to your `pom.xml`:

Expand All @@ -57,7 +57,7 @@ The dependency:
<dependency>
<groupId>io.github.erdos</groupId>
<artifactId>stencil-core</artifactId>
<version>0.3.31</version>
<version>0.5.0</version>
</dependency>
```

Expand All @@ -72,7 +72,7 @@ And the [Clojars](https://clojars.org) repository:

Alternatively, if you are using Leiningen, add the following to
the `:dependencies` section of your `project.clj`
file: `[io.github.erdos/stencil-core "0.3.31"]`
file: `[io.github.erdos/stencil-core "0.5.0"]`

Previous versions are available on the [Stencil Clojars](https://clojars.org/io.github.erdos/stencil-core) page.

Expand Down
9 changes: 9 additions & 0 deletions docs/Functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This is a short description of the functions implemented in Stencil:
- [list](#list)
- [lowercase](#lowercase)
- [map](#map)
- [pageBreak](#pagebreak)
- `percent`
- `range`
- [round](#round)
Expand Down Expand Up @@ -164,6 +165,10 @@ The rendering throws an exception on invalid HTML input or unexpected HTML tags.
Write the following to embed the content of `x` as HTML in the document:
- <code>{<i>%=html(x) %</i>}</code>.

### PageBreak

Inserts a page break at the place of the call. Example: <code>{<i>%=pageBreak()%</i>}</code>

### XML

You can embed custom xml fragments in the document with the `xml()` function. The parameter is a string containing the XML nodes to insert.
Expand Down Expand Up @@ -200,6 +205,10 @@ The `lowercase(x)` function turns its string argument into a lowercase string. F

The `str(x)` functions convers its non-null arguments into a string. Returns an empty string when all arguments are null.

## Replace

The `replace(text, pattern, replacement)` function replaces all occurrence of `pattern` in `text` by `replacement`.

## Numeric functions

### Round
Expand Down
7 changes: 7 additions & 0 deletions docs/Syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ Syntax:
In this example we iterate over the contents of the `elements` array.
The text `BODY` is inserted for every element.

### Iteration with index

A special syntax is implemented for iterating over a collection (vector or map) with the indices bound to a new variable. Syntax:

- <code>{<i>%for idx, x in elements %</i>}BODY{<i>%end%</i>}</code>


## Finding errors

- Check that every control structure is properly closed!
Expand Down
2 changes: 1 addition & 1 deletion java-src/io/github/erdos/stencil/TemplateData.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static TemplateData fromMap(Map<String, Object> data) {
*
* @return template data map. Not null.
*/
public final Map<String, Object> getData() {
public Map<String, Object> getData() {
return data;
}
}
7 changes: 3 additions & 4 deletions java-src/io/github/erdos/stencil/TemplateVariables.java
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,13 @@ private List<SchemaError> validate(Object data, Node schema) {
return validateImpl("", data, schema).collect(toList());
}

@SuppressWarnings("unchecked")
private Stream<SchemaError> validateImpl(String path, Object data, Node schema) {
return schema.accept(new NodeVisitor<Stream<SchemaError>>() {
@Override
public Stream<SchemaError> visitArray(Node wrapped) {
if (data instanceof List) {
final AtomicInteger index = new AtomicInteger();
return ((List) data).stream().flatMap(x -> {
return ((List<?>) data).stream().flatMap(x -> {
final String newPath = path + "[" + index.getAndIncrement() + "]";
return validateImpl(newPath, x, wrapped);
});
Expand All @@ -170,7 +169,7 @@ public Stream<SchemaError> visitArray(Node wrapped) {
@Override
public Stream<SchemaError> visitMap(Map<String, Node> items) {
if (data instanceof Map) {
Map dataMap = (Map) data;
Map<?, ?> dataMap = (Map<?, ?>) data;
return items.entrySet().stream().flatMap(schemaEntry -> {
if (dataMap.containsKey(schemaEntry.getKey())) {
return validateImpl(path + "." + schemaEntry.getKey(), dataMap.get(schemaEntry.getKey()), schemaEntry.getValue());
Expand All @@ -192,7 +191,7 @@ public Stream<SchemaError> visitLeaf() {
}

@SuppressWarnings("unused")
class SchemaError {
private static final class SchemaError {
private final String path;
private final String msg;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
*/
public final class EvalException extends RuntimeException {

public EvalException(String message, Exception cause) {
super(message, cause);
}

private EvalException(Exception cause) {
super(cause);
}

private EvalException(String message) {
super(message);
}

public static EvalException wrapping(Exception cause) {
return new EvalException(cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ else if (expr != null && expr.equals(value))
},

/**
* Returns the first non-null an non-empty value.
* Returns the first non-null a non-empty value.
* <p>
* Accepts any arguments. Skips null values, empty strings and empty collections.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public enum LocaleFunctions implements Function {


/**
* Formats number as localized currency. An optional second argument can be used to specify locale code.
* Formats number as a localized monetary amount with currency. An optional second argument can be used to specify locale code.
* Returns a string.
* <p>
* Usage: currency(34) or currency(34, "HU")
Expand All @@ -23,7 +23,7 @@ public Object call(Object... arguments) throws IllegalArgumentException {
},

/**
* Formats number as localized percent. An optional second argument can be used to specify locale code.
* Formats number as a localized percentage value. An optional second argument can be used to specify locale code.
* Returns a string.
* <p>
* Usage: percent(34) or percent(34, "HU")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.util.Arrays;
import java.util.Collection;
import java.util.IllegalFormatException;
import java.util.Objects;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -58,7 +57,7 @@ public Object call(Object... arguments) throws IllegalArgumentException {
StringBuilder builder = new StringBuilder();
for (Object argument : arguments) {
if (argument != null)
builder.append(argument.toString());
builder.append(argument);
}
return builder.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ public PreparedTemplate prepareTemplateFile(File inputTemplateFile, PrepareOptio
.orElseThrow(() -> new IllegalArgumentException("Can not build template file: " + inputTemplateFile));
}

private final class DelayedContainer<X> implements Delayed {
private static final class DelayedContainer<X> implements Delayed {
private final long expiration;
private final X contents;

Expand Down
8 changes: 4 additions & 4 deletions java-src/io/github/erdos/stencil/impl/FileHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static String removeExtension(File f) {
* @return a new file object pointing to a non-existing file in temp directory.
*/
public static File createNonexistentTempFile(String prefix, String suffix) {
return new File(TEMP_DIRECTORY, prefix + UUID.randomUUID().toString() + suffix);
return new File(TEMP_DIRECTORY, prefix + UUID.randomUUID() + suffix);
}

/**
Expand Down Expand Up @@ -101,7 +101,7 @@ public static void forceDelete(final File file) {
* @param file to delete, not null
* @throws NullPointerException on null or invalid file
*/
@SuppressWarnings({"ResultOfMethodCallIgnored", "ConstantConditions"})
@SuppressWarnings({"ConstantConditions"})
public static void forceDeleteOnExit(final File file) {
file.deleteOnExit();
if (file.isDirectory()) {
Expand All @@ -113,7 +113,7 @@ public static void forceDeleteOnExit(final File file) {

/**
* Returns a string representation of path with unix separators ("/") instead of the
* system-dependent separators (which is backslash on windows.)
* system-dependent separators (which is backslash on Windows).
*
* @param path not null path object
* @return string of path with slash separators
Expand All @@ -128,7 +128,7 @@ public static String toUnixSeparatedString(Path path) {
// on unix systems
return path.toString();
} else {
// on windows systems we replace backslash with slashes
// on Windows systems we replace backslash with slashes
return path.toString().replaceAll(Pattern.quote(separator), "/");
}
}
Expand Down
13 changes: 8 additions & 5 deletions java-src/io/github/erdos/stencil/impl/NativeEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import clojure.lang.AFunction;
import clojure.lang.IFn;
import clojure.lang.PersistentHashMap;
import io.github.erdos.stencil.*;
import io.github.erdos.stencil.exceptions.EvalException;
import io.github.erdos.stencil.functions.FunctionEvaluator;
Expand Down Expand Up @@ -49,13 +50,15 @@ public EvaluatedDocument render(PreparedTemplate template, Map<String, PreparedF
stopwatch.accept(() -> "Starting document rendering for template " + template.getTemplateFile());

final IFn fn = ClojureHelper.findFunction("eval-template");
final Map argsMap = makeArgsMap(template.getSecretObject(), fragments, data.getData());
final Object argsMap = makeArgsMap(template.getSecretObject(), fragments, data.getData());

final Map result;
try {
result = (Map) fn.invoke(argsMap);
} catch (EvalException e) {
throw e;
} catch (Exception e) {
throw EvalException.wrapping(e);
throw new EvalException("Unexpected error", e);
}

final Consumer<OutputStream> stream = resultWriter(result);
Expand Down Expand Up @@ -94,7 +97,7 @@ private static Consumer<OutputStream> resultWriter(Map result) {
}

@SuppressWarnings("unchecked")
private Map makeArgsMap(Object template, Map<String, PreparedFragment> fragments, Object data) {
private Object makeArgsMap(Object template, Map<String, PreparedFragment> fragments, Object data) {
final Map result = new HashMap();
result.put(ClojureHelper.Keywords.TEMPLATE.kw, template);
result.put(ClojureHelper.Keywords.DATA.kw, data);
Expand All @@ -103,9 +106,9 @@ private Map makeArgsMap(Object template, Map<String, PreparedFragment> fragments
// string to clojure map
final Map<String, Object> kvs = fragments.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue().getImpl()));
result.put(ClojureHelper.Keywords.FRAGMENTS.kw, kvs);
result.put(ClojureHelper.Keywords.FRAGMENTS.kw, PersistentHashMap.create(kvs));

return result;
return PersistentHashMap.create(result);
}

private final class FunctionCaller extends AFunction {
Expand Down
29 changes: 14 additions & 15 deletions java-src/io/github/erdos/stencil/impl/NativeTemplateFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,11 @@ public PreparedFragment prepareFragmentFile(final File fragmentFile, PrepareOpti
final Map<Keyword, Object> prepared;

try {
//noinspection unchecked
prepared = (Map<Keyword, Object>) prepareFunction.invoke(fragmentFile, options);
} catch (ParsingException e) {
prepared = invokePrepareFunction(fragmentFile, options);
} catch (ParsingException | IOException e) {
throw e;
} catch (Exception e) {
//noinspection ConstantConditions
if (e instanceof IOException) {
// possible because of Clojure magic :-(
throw (IOException) e;
} else {
throw ParsingException.wrapping("Could not parse template file!", e);
}
throw ParsingException.wrapping("Could not parse template file!", e);
}

final File zipDirResource = (File) prepared.get(ClojureHelper.Keywords.SOURCE_FOLDER.kw);
Expand All @@ -70,25 +63,31 @@ public PreparedFragment prepareFragmentFile(final File fragmentFile, PrepareOpti
return new PreparedFragment(prepared, zipDirResource);
}

@SuppressWarnings({"unchecked", "RedundantThrows"})
private static Map<Keyword, Object> invokePrepareFunction(File fragmentFile, PrepareOptions options) throws Exception {
final IFn prepareFunction = ClojureHelper.findFunction("prepare-fragment");
return (Map<Keyword, Object>) prepareFunction.invoke(fragmentFile, options);
}

/**
* Retrieves content of :variables keyword from map as a set.
*/
@SuppressWarnings("unchecked")
private Set variableNames(Map prepared) {
private static Set<String> variableNames(Map prepared) {
return prepared.containsKey(ClojureHelper.Keywords.VARIABLES.kw)
? unmodifiableSet(new HashSet<Set>((Collection) prepared.get(ClojureHelper.Keywords.VARIABLES.kw)))
? unmodifiableSet(new HashSet<>((Collection<String>) prepared.get(ClojureHelper.Keywords.VARIABLES.kw)))
: emptySet();
}

@SuppressWarnings("unchecked")
private Set fragmentNames(Map prepared) {
private static Set<String> fragmentNames(Map prepared) {
return prepared.containsKey(ClojureHelper.Keywords.FRAGMENTS.kw)
? unmodifiableSet(new HashSet<Set>((Collection) prepared.get(ClojureHelper.Keywords.FRAGMENTS.kw)))
? unmodifiableSet(new HashSet<>((Collection<String>) prepared.get(ClojureHelper.Keywords.FRAGMENTS.kw)))
: emptySet();
}

@SuppressWarnings("unchecked")
private PreparedTemplate prepareTemplateImpl(TemplateDocumentFormats templateDocFormat, InputStream input, File originalFile, PrepareOptions options) {
private static PreparedTemplate prepareTemplateImpl(TemplateDocumentFormats templateDocFormat, InputStream input, File originalFile, PrepareOptions options) {
final IFn prepareFunction = ClojureHelper.findFunction("prepare-template");

final String format = templateDocFormat.name();
Expand Down

0 comments on commit be9f9dd

Please sign in to comment.