com.greenpepper.runner.dialect.MarkdownDialect Maven / Gradle / Ivy
The newest version!
package com.greenpepper.runner.dialect;
import com.greenpepper.dialect.SpecificationDialect;
import com.greenpepper.dialect.SpecificationDialectException;
import com.greenpepper.shaded.com.vladsch.flexmark.ast.Node;
import com.greenpepper.shaded.com.vladsch.flexmark.ext.tables.TablesExtension;
import com.greenpepper.shaded.com.vladsch.flexmark.html.HtmlRenderer;
import com.greenpepper.shaded.com.vladsch.flexmark.parser.Parser;
import com.greenpepper.shaded.com.vladsch.flexmark.util.options.MutableDataSet;
import com.greenpepper.shaded.org.apache.commons.io.IOUtils;
import com.greenpepper.shaded.org.jsoup.Jsoup;
import com.greenpepper.shaded.org.jsoup.nodes.Document;
import com.greenpepper.shaded.org.slf4j.Logger;
import com.greenpepper.shaded.org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Collections;
import static java.lang.String.format;
import static com.greenpepper.shaded.org.apache.commons.lang3.StringUtils.isBlank;
import static com.greenpepper.shaded.org.apache.commons.lang3.StringUtils.isNotBlank;
public class MarkdownDialect implements SpecificationDialect {
private static final Logger LOGGER = LoggerFactory.getLogger(MarkdownDialect.class);
private static final String EMPTY_STRING = "";
private final Parser parser;
private final HtmlRenderer renderer;
private String cssPath;
private String jsPath;
private Charset usedCharset;
public MarkdownDialect() {
MutableDataSet options = new MutableDataSet()
.set(Parser.EXTENSIONS, Collections.singletonList(TablesExtension.create()));
parser = Parser.builder(options).build();
renderer = HtmlRenderer.builder(options).build();
}
public MarkdownDialect(String ... args) {
this();
if (args.length > 0) {
cssPath = args[0];
}
if (args.length > 1) {
jsPath = args[1];
}
}
@Override
public String convert(InputStream input) throws SpecificationDialectException {
try {
usedCharset = Charset.forName("UTF-8");
ByteArrayOutputStream duplicated = new ByteArrayOutputStream();
IOUtils.copy(input, duplicated);
// test the UTF8 charset
byte[] bytes = duplicated.toByteArray();
usedCharset = CharsetDetector.detectCharset(new ByteArrayInputStream(bytes), usedCharset);
if (usedCharset == null) {
Collection charsets = Charset.availableCharsets().values();
usedCharset = CharsetDetector.detectCharset(new ByteArrayInputStream(bytes), charsets);
}
if (usedCharset == null) {
usedCharset = Charset.defaultCharset();
}
LOGGER.debug("Using charset {}", usedCharset);
InputStreamReader streamReader = new InputStreamReader(new ByteArrayInputStream(bytes), usedCharset);
if (!streamReader.ready()) {
return EMPTY_STRING;
}
Node document = parser.parseReader(streamReader);
String render = renderer.render(document);
// Ensure the result starts with html tags
Document jsoupDoc = Jsoup.parse(render);
jsoupDoc.head().appendElement("style");
if (isNotBlank(jsPath)) {
jsoupDoc.head().appendElement("script");
}
org.w3c.dom.Document w3cDoc = parse(jsoupDoc.toString());
w3cDoc.getElementsByTagName("style").item(0).setTextContent(readMDCss());
if (isNotBlank(jsPath)) {
w3cDoc.getElementsByTagName("script").item(0).setTextContent(readMDJs());
}
String htmlPage = toString(w3cDoc);
LOGGER.debug("converted HTML:\n {}", htmlPage);
return htmlPage;
} catch (SpecificationDialectException e) {
throw e;
} catch (Exception e) {
throw new SpecificationDialectException("Can't parse the input.", e);
}
}
private String readMDCss() throws IOException {
String cssFileContent;
if (isBlank(cssPath)) {
InputStream resourceAsStream = getClass().getResourceAsStream("mddialect.css");
cssFileContent = IOUtils.toString(resourceAsStream, usedCharset);
} else {
cssFileContent = readFromResource(cssPath);
}
LOGGER.debug("Read CSS content:\n{}", cssFileContent);
return cssFileContent;
}
private String readMDJs() throws IOException {
if (isBlank(jsPath)) {
return "";
}
String jsContent = readFromResource(jsPath);
LOGGER.debug("Read JSContent:\n{}", jsContent);
return jsContent;
}
private String readFromResource(String jsPath) throws IOException {
String jsContent;
if (jsPath.contains(":")) {
jsContent = IOUtils.toString(new URL(jsPath), usedCharset);
} else {
// search in classpath
InputStream stream = getClass().getClassLoader().getResourceAsStream(jsPath);
if (stream == null) {
throw new SpecificationDialectException(format("Unable to get the specified resource in the classpath: %s", jsPath));
}
jsContent = IOUtils.toString(stream, usedCharset);
}
return jsContent;
}
private org.w3c.dom.Document parse(String xml) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(false);
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
return documentBuilder.parse(new ByteArrayInputStream(xml.getBytes(usedCharset)));
}
private String toString(org.w3c.dom.Document document) throws TransformerException {
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
DOMSource domSource = new DOMSource(document);
transformer.transform(domSource, result);
return writer.toString();
}
}