diff --git a/src/generator/com/elovirta/pdf/merge.xsl b/src/generator/com/elovirta/pdf/merge.xsl index 348e77e..3e6d33e 100644 --- a/src/generator/com/elovirta/pdf/merge.xsl +++ b/src/generator/com/elovirta/pdf/merge.xsl @@ -22,7 +22,10 @@ - + + + diff --git a/src/main/java/com/elovirta/pdf/ant/StylesheetGeneratorTask.java b/src/main/java/com/elovirta/pdf/ant/StylesheetGeneratorTask.java index abd4c5d..79ce4ef 100644 --- a/src/main/java/com/elovirta/pdf/ant/StylesheetGeneratorTask.java +++ b/src/main/java/com/elovirta/pdf/ant/StylesheetGeneratorTask.java @@ -12,11 +12,13 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; -import java.io.*; +import java.io.File; +import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; @@ -28,7 +30,9 @@ public class StylesheetGeneratorTask extends Task { private static final QName ATTR = QName.fromClarkName("{}attr"); private static final QName VERSION = QName.fromClarkName("{}version"); private static final QName FORMATTER = QName.fromClarkName("{}formatter"); - /** Magic extends for build-in theme. */ + /** + * Magic extends for build-in theme. + */ private static final String DEFAULT_EXTENDS = "default"; private static final URI DEFAULT_EXTENDS_URI = URI.create("classpath:/com/elovirta/pdf/default.json"); @@ -38,6 +42,7 @@ public class StylesheetGeneratorTask extends Task { private URIResolver resolver; private Processor processor; private XsltCompiler compiler; + private Xslt30Transformer transformer; private XPathCompiler xpathCompiler; private ObjectMapper yamlReader; private ObjectMapper jsonWriter; @@ -53,6 +58,12 @@ public void init() { processor = xmlUtils.getProcessor(); compiler = processor.newXsltCompiler(); compiler.setURIResolver(resolver); + try { + final XsltExecutable executable = compiler.compile(resolver.resolve("classpath:/com/elovirta/pdf/merge.xsl", null)); + transformer = executable.load30(); + } catch (SaxonApiException | TransformerException e) { + throw new BuildException(String.format("Failed to parse template %s", template), e); + } xpathCompiler = processor.newXPathCompiler(); yamlReader = new ObjectMapper(new YAMLFactory()); jsonWriter = new ObjectMapper(); @@ -69,6 +80,7 @@ public void execute() throws BuildException { } final XdmItem xdmItem = parseTemplate(); + getProject().log("Template:" + xdmItem, Project.MSG_INFO); generate(xdmItem, "front-matter.xsl", "xsl/fo/front-matter.xsl", null); generate(xdmItem, "commons.xsl", "xsl/fo/commons.xsl", null); generate(xdmItem, "tables.xsl", "xsl/fo/tables.xsl", null); @@ -150,24 +162,18 @@ File generate(final XdmItem xdmItem, XdmItem parseTemplate() { getProject().log(this, String.format("Reading %s", template.toURI()), Project.MSG_INFO); final String name = template.getName().toLowerCase(); - try { - final XsltExecutable executable = compiler.compile(resolver.resolve("classpath:/com/elovirta/pdf/merge.xsl", null)); - final XdmItem merged; - // XXX: Saxon's JSON functions don't use URIResolver, so have to parse manually - if (name.endsWith(".yml") || name.endsWith(".yaml")) { - merged = parseYamlTemplate(executable, parseYaml(template.toURI()), template.toURI()); - } else { - merged = parseJsonTemplate(); - } - return resolveVariables(executable, merged); - } catch (SaxonApiException | TransformerException e) { - throw new BuildException(String.format("Failed to parse template %s", template), e); + final XdmItem merged; + // XXX: Saxon's JSON functions don't use URIResolver, so have to parse manually + if (name.endsWith(".yml") || name.endsWith(".yaml")) { + merged = parseYamlTemplate(parseYaml(template.toURI()), template.toURI()); + } else { + merged = parseJsonTemplate(); } + return resolveVariables(merged); } - private XdmItem resolveVariables(final XsltExecutable executable, final XdmItem base) { + private XdmItem resolveVariables(final XdmItem base) { try { - final Xslt30Transformer transformer = executable.load30(); final XdmValue resolved = transformer.callFunction(QName.fromClarkName("{x}resolve"), new XdmValue[]{ base }); @@ -182,6 +188,7 @@ private XdmItem resolveVariables(final XsltExecutable executable, final XdmItem private XdmItem parseJsonTemplate() { try { + new JsonBuilder(xmlUtils.getProcessor().getUnderlyingConfiguration()); final XdmItem theme = xpathCompiler.evaluateSingle("json-doc(.)", new XdmAtomicValue(template.toURI())); final XsltExecutable executable = compiler.compile(resolver.resolve("classpath:/com/elovirta/pdf/merge.xsl", null)); final Xslt30Transformer transformer = executable.load30(); @@ -196,54 +203,53 @@ private XdmItem parseJsonTemplate() { } } - private XdmItem parseYamlTemplate(final XsltExecutable executable, final XdmItem base, final URI url) { + private XdmItem parseYamlTemplate(final XdmItem base, final URI url) { try { - final Xslt30Transformer transformer = executable.load30(); final XdmItem extendsValue = xpathCompiler.evaluateSingle(". ?extends", base); if (extendsValue != null && extendsValue.getStringValue().equals(DEFAULT_EXTENDS)) { - final XdmItem extendsRes = xpathCompiler.evaluateSingle("json-doc(.)", new XdmAtomicValue(DEFAULT_EXTENDS_URI)); + final XPathSelector selector = xpathCompiler.compile("json-doc(.)").load(); + selector.setURIResolver(resolver); + selector.setContextItem(new XdmAtomicValue(DEFAULT_EXTENDS_URI)); + final XdmItem extendsRes = selector.evaluateSingle(); +// final XdmItem extendsRes = xpathCompiler.evaluateSingle("json-doc(.)", new XdmAtomicValue(DEFAULT_EXTENDS_URI)); - final XdmValue flattened = transformer.callFunction(QName.fromClarkName("{x}flatten"), new XdmValue[]{ - base - }); - final XdmValue normalized = transformer.callFunction(QName.fromClarkName("{x}normalize"), new XdmValue[]{ - flattened, XdmEmptySequence.getInstance(), new XdmAtomicValue(DEFAULT_EXTENDS_URI) - }); - return normalized.itemAt(0); + return transformer.callFunction(QName.fromClarkName("{x}merge"), new XdmValue[]{ + normalize(extendsRes), + normalize(base) + }).itemAt(0); } else if (extendsValue != null) { final URI extendsUri = url.resolve(extendsValue.getStringValue()); - final XdmItem extendsRes = parseYamlTemplate(executable, parseYaml(extendsUri), url); + final XdmItem extendsRes = parseYamlTemplate(parseYaml(extendsUri), url); - final XdmValue flattened = transformer.callFunction(QName.fromClarkName("{x}flatten"), new XdmValue[]{ - base - }); - final XdmValue normalized = transformer.callFunction(QName.fromClarkName("{x}normalize"), new XdmValue[]{ - flattened, XdmEmptySequence.getInstance(), new XdmAtomicValue(extendsUri) - }); return transformer.callFunction(QName.fromClarkName("{x}merge"), new XdmValue[]{ - extendsRes.stream().asXdmValue(), normalized + extendsRes, normalize(base) }).itemAt(0); } else { - final XdmValue flattened = transformer.callFunction(QName.fromClarkName("{x}flatten"), new XdmValue[]{ - base - }); - final XdmValue normalized = transformer.callFunction(QName.fromClarkName("{x}normalize"), new XdmValue[]{ - flattened, XdmEmptySequence.getInstance(), new XdmAtomicValue(url) - }); - return normalized.itemAt(0); + return normalize(base).itemAt(0); } } catch (SaxonApiException e) { throw new BuildException(String.format("Failed to parse template %s", template), e); } } + private XdmItem normalize(XdmItem in) { + try { + return transformer.callFunction(QName.fromClarkName("{x}normalize"), new XdmValue[]{ + transformer.callFunction(QName.fromClarkName("{x}flatten"), new XdmValue[]{ + in + }), XdmEmptySequence.getInstance(), new XdmAtomicValue(DEFAULT_EXTENDS_URI) + }).itemAt(0); + } catch (SaxonApiException e) { + throw new RuntimeException(e); + } + } + private XdmItem parseYaml(final URI abs) { try { final Object yaml = yamlReader.readValue(abs.toURL(), Object.class); final String json = jsonWriter.writeValueAsString(yaml); return xpathCompiler.evaluateSingle("parse-json(.)", new XdmAtomicValue(json)); } catch (SaxonApiException | IOException e) { - e.printStackTrace(); throw new BuildException("Failed to convert YAML to JSON: " + e.getMessage(), e); } } diff --git a/src/main/resources/com/elovirta/pdf/default.json b/src/main/resources/com/elovirta/pdf/default.json index 9d3b971..3456a45 100644 --- a/src/main/resources/com/elovirta/pdf/default.json +++ b/src/main/resources/com/elovirta/pdf/default.json @@ -1,7 +1,7 @@ { "style": { - "toc-1": { - "color": "pink" + "h1": { + "font-weight": "bold" } } } diff --git a/src/test/java/com/elovirta/pdf/ant/StylesheetGeneratorTaskTest.java b/src/test/java/com/elovirta/pdf/ant/StylesheetGeneratorTaskTest.java index 811ab34..53550e3 100644 --- a/src/test/java/com/elovirta/pdf/ant/StylesheetGeneratorTaskTest.java +++ b/src/test/java/com/elovirta/pdf/ant/StylesheetGeneratorTaskTest.java @@ -5,7 +5,6 @@ import org.dita.dost.log.DITAOTAntLogger; import org.dita.dost.util.XMLUtils; import org.json.JSONException; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; @@ -13,7 +12,6 @@ import org.skyscreamer.jsonassert.JSONCompareMode; import java.io.*; -import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -34,6 +32,7 @@ public class StylesheetGeneratorTaskTest { @BeforeEach public void setUp() { xmlUtils = new XMLUtils(); + xmlUtils.getProcessor().getUnderlyingConfiguration().setURIResolver(resolver); xpathCompiler = xmlUtils.getProcessor().newXPathCompiler(); final Project project = new Project(); xmlUtils.setLogger(new DITAOTAntLogger(project)); @@ -174,6 +173,20 @@ public void getTemplate_normalize_single(final String template) throws URISyntax JSONCompareMode.STRICT); } + @ParameterizedTest + @ValueSource(strings = { + "default.json", + "default.yaml" + }) + public void getTemplate_default(final String template) throws URISyntaxException, SaxonApiException, JSONException { + task.setTemplate(new File(getClass().getClassLoader().getResource("src/" + template).toURI()).getAbsolutePath()); + + final XdmValue act = task.parseTemplate(); + + assertEquals(readToString("exp/" + template.substring(0, template.indexOf(".")) + ".json"), toString(act), + JSONCompareMode.STRICT); + } + private String readToString(final String name) { final InputStream baseInput = getClass().getClassLoader().getResourceAsStream(name); return new BufferedReader(new InputStreamReader(baseInput, StandardCharsets.UTF_8)) diff --git a/src/test/resources/com/elovirta/pdf/default.json b/src/test/resources/com/elovirta/pdf/default.json new file mode 100644 index 0000000..ce30cb3 --- /dev/null +++ b/src/test/resources/com/elovirta/pdf/default.json @@ -0,0 +1,7 @@ +{ + "style": { + "h1": { + "font-weight": "bold", + } + } +} diff --git a/src/test/resources/exp/default.json b/src/test/resources/exp/default.json new file mode 100644 index 0000000..e7bb2b7 --- /dev/null +++ b/src/test/resources/exp/default.json @@ -0,0 +1,10 @@ +{ + "style": { + "toc-1": { + "color": "blue" + }, + "toc-2": { + "color": "green" + } + } +} \ No newline at end of file diff --git a/src/test/resources/src/default.json b/src/test/resources/src/default.json new file mode 100644 index 0000000..791dd7a --- /dev/null +++ b/src/test/resources/src/default.json @@ -0,0 +1,8 @@ +{ + "extends": "default", + "style": { + "h1": { + "background-color": "pink" + } + } +} \ No newline at end of file diff --git a/src/test/resources/src/default.yaml b/src/test/resources/src/default.yaml new file mode 100644 index 0000000..b083efd --- /dev/null +++ b/src/test/resources/src/default.yaml @@ -0,0 +1,4 @@ +extends: default +style: + h1: + background-color: pink