All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metreeca.mark.steps.Jade Maven / Gradle / Ivy

/*
 * Copyright © 2019-2020 Metreeca srl. All rights reserved.
 */

package com.metreeca.mark.steps;

import com.metreeca.mark.Mark;

import com.vladsch.flexmark.util.sequence.SubSequence;
import de.neuland.jade4j.JadeConfiguration;
import de.neuland.jade4j.exceptions.ExpressionException;
import de.neuland.jade4j.expression.ExpressionHandler;
import de.neuland.jade4j.model.JadeModel;
import de.neuland.jade4j.template.TemplateLoader;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
import static java.util.Collections.singletonMap;


public final class Jade {

	private static final Pattern ExpressionPattern=Pattern.compile("\\\\?\\$\\{([.\\w]+)}");


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	private final Mark mark;

	private final JadeConfiguration jade;


	public Jade(final Mark mark) {

		if ( mark == null ) {
			throw new NullPointerException("null mark");
		}

		this.mark=mark;
		this.jade=new JadeConfiguration();

		jade.setPrettyPrint(false);
		jade.setSharedVariables(mark.shared());

		jade.setTemplateLoader(new TemplateLoader() {

			@Override public long getLastModified(final String name) throws IOException {
				return Files.getLastModifiedTime(mark.layout(name)).toMillis();
			}

			@Override public Reader getReader(final String name) throws IOException {
				return Files.newBufferedReader(mark.layout(name), UTF_8);
			}

			@Override public String getExtension() {
				return "jade";
			}

		});

	}


	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	public Path write(final Path target, final Map model) {

		if ( target == null ) {
			throw new NullPointerException("null target");
		}

		if ( model == null ) {
			throw new NullPointerException("null model");
		}

		final String layout=model.getOrDefault("layout", mark.layout()).toString();

		try ( final BufferedWriter writer=Files.newBufferedWriter(target, UTF_8) ) {

			jade.renderTemplate(jade.getTemplate(layout), page(model, target), writer);

		} catch ( final IOException e ) {
			throw new UncheckedIOException(e);
		}

		return target;
	}


	//// Variable Replacement //////////////////////////////////////////////////////////////////////////////////////////

	private Map page(final Map model, final Path target) {

		model.put("base", mark.base(target).toString());
		model.put("path", mark.path(target).toString());

		model.computeIfAbsent("date", key -> ISO_LOCAL_DATE.format(LocalDate.now()));
		model.computeIfPresent("content", (key, content) -> evaluate(content.toString(), model));

		return singletonMap("page", model);
	}

	private String evaluate(final CharSequence chars, final Map model) {

		final Matcher matcher=ExpressionPattern.matcher(chars);
		final ExpressionHandler handler=jade.getExpressionHandler();

		final StringBuilder builder=new StringBuilder(chars.length());

		int last=0;

		while ( matcher.find() ) {

			final int start=matcher.start();
			final int end=matcher.end();

			builder.append(chars.subSequence(last, start)); // leading text

			if ( matcher.group().charAt(0) == '\\' ) { // escaped

				builder.append(chars.subSequence(start+1, end)); // expression text

			} else {

				try {

					final JadeModel bindings=new JadeModel(jade.getSharedVariables());

					bindings.putAll(model);

					builder.append(SubSequence.of(handler.evaluateStringExpression(
							matcher.group(1), bindings // expression value
					)));

				} catch ( final ExpressionException e ) {
					throw new RuntimeException(e);
				}

			}

			last=end;
		}

		builder.append(chars.subSequence(last, chars.length())); // trailing text

		return builder.toString();
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy