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

com.xmlcalabash.extensions.AsciiDoctor Maven / Gradle / Ivy

package com.xmlcalabash.extensions;

import com.xmlcalabash.core.XMLCalabash;
import com.xmlcalabash.core.XProcConstants;
import com.xmlcalabash.core.XProcException;
import com.xmlcalabash.core.XProcRuntime;
import com.xmlcalabash.io.ReadablePipe;
import com.xmlcalabash.io.WritablePipe;
import com.xmlcalabash.library.DefaultStep;
import com.xmlcalabash.runtime.XAtomicStep;
import com.xmlcalabash.util.Base64;
import com.xmlcalabash.util.S9apiUtils;
import com.xmlcalabash.util.XProcURIResolver;
import net.sf.saxon.s9api.DocumentBuilder;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;

import static org.asciidoctor.Asciidoctor.Factory.create;

import net.sf.saxon.s9api.XdmNode;
import org.asciidoctor.Asciidoctor;
import org.asciidoctor.Attributes;
import org.asciidoctor.CompatMode;
import org.asciidoctor.Options;
import org.asciidoctor.Placement;
import org.asciidoctor.SafeMode;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXSource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

/**
 * Created by ndw on 4/14/15.
 */

@XMLCalabash(
        name = "cx:asciidoctor",
        type = "{http://xmlcalabash.com/ns/extensions}asciidoctor")

public class AsciiDoctor extends DefaultStep {
    /* Attributes */
    private static final QName _allow_read_uri = new QName("", "allow-read-uri");
    private static final QName _attribute_missing = new QName("", "attribute-missing");
    private static final QName _attribute_undefined = new QName("", "attribute-undefined");
    private static final QName _backend = new QName("", "backend");
    private static final QName _title = new QName("", "title");
    private static final QName _doctype = new QName("", "doctype");
    private static final QName _imagesdir = new QName("", "imagesdir");
    private static final QName _source_language = new QName("", "source-language");
    private static final QName _source_highlighter = new QName("", "source-highlighter");
    private static final QName _max_include_depth = new QName("", "max-include-depth");
    private static final QName _sectnumlevels = new QName("", "sectnumlevels");
    private static final QName _hardbreaks = new QName("", "hardbreaks");
    private static final QName _cache_uri = new QName("", "cache-uri");
    private static final QName _hide_uri_scheme = new QName("", "hide-uri-scheme");
    private static final QName _appendix_caption = new QName("", "appendix-caption");
    private static final QName _math = new QName("", "math");
    private static final QName _skip_front_matter = new QName("", "skip-front-matter");
    private static final QName _setanchors = new QName("", "setanchors");
    private static final QName _untitled_label = new QName("", "untitled-label");
    private static final QName _ignore_undefined = new QName("", "ignore-undefined");
    private static final QName _toc_placement = new QName("", "toc-placement");
    private static final QName _toc2_placement = new QName("", "toc2-placement");
    private static final QName _showtitle = new QName("", "showtitle");
    private static final QName _toc = new QName("", "toc");
    private static final QName _localdate = new QName("", "localdate");
    private static final QName _localtime = new QName("", "localtime");
    private static final QName _docdate = new QName("", "docdate");
    private static final QName _doctime = new QName("", "doctime");
    private static final QName _stylesheet = new QName("", "stylesheet");
    private static final QName _stylesdir = new QName("", "stylesdir");
    private static final QName _linkcss = new QName("", "linkcss");
    private static final QName _copycss = new QName("", "copycss");
    private static final QName _icons = new QName("", "icons");
    private static final QName _iconfont_remote = new QName("", "iconfont-remote");
    private static final QName _iconfont_cdn = new QName("", "iconfont-cdn");
    private static final QName _iconfont_name = new QName("", "iconfont-name");
    private static final QName _data_uri = new QName("", "data-uri");
    private static final QName _iconsdir = new QName("", "iconsdir");
    private static final QName _numbered = new QName("", "numbered");
    private static final QName _linkattrs = new QName("", "linkattrs");
    private static final QName _experimental = new QName("", "experimental");
    private static final QName _nofooter = new QName("", "nofooter");
    private static final QName _compat_mode = new QName("", "compat-mode");

    /* Options */
    private static final QName _header_footer = new QName("", "header-footer");
    private static final QName _template_dirs = new QName("", "template-dirs");
    private static final QName _template_engine = new QName("", "template-engine");
    private static final QName _safe = new QName("", "safe");
    private static final QName _eruby = new QName("", "eruby");
    private static final QName _compact = new QName("", "compact");
    private static final QName _base_dir = new QName("", "base-dir");
    private static final QName _template_cache = new QName("", "template-cache");
    private static final QName _parse_header_only = new QName("", "parse-header-only");

    private static final String library_xpl = "http://xmlcalabash.com/extension/steps/asciidoctor.xpl";
    private static final String library_url = "/com/xmlcalabash/extensions/asciidoctor/library.xpl";
    private static final QName _content_type = new QName("content-type");
    private ReadablePipe source = null;
    private WritablePipe result = null;

    public AsciiDoctor(XProcRuntime runtime, XAtomicStep step) {
        super(runtime, step);
    }

    public void setInput(String port, ReadablePipe pipe) {
        source = pipe;
    }

    public void setOutput(String port, WritablePipe pipe) {
        result = pipe;
    }

    public void reset() {
        source.resetReader();
        result.resetWriter();
    }

    public void run() throws SaxonApiException {
        super.run();

        XdmNode doc = source.read();
        XdmNode root = S9apiUtils.getDocumentElement(doc);
        String asciiDoc = null;

        if ((XProcConstants.c_data.equals(root.getNodeName())
                && "application/octet-stream".equals(root.getAttributeValue(_content_type)))
                || "base64".equals(root.getAttributeValue(_encoding))) {
            byte[] decoded = Base64.decode(root.getStringValue());
            asciiDoc = new String(decoded);
        } else {
            asciiDoc = root.getStringValue();
        }

        Attributes adAttr = attributes();
        Options adOpts = options();

        adOpts.setAttributes(adAttr);

        Asciidoctor asciidoctor = create();
        String doctored = asciidoctor.convert(asciiDoc, adOpts);

        try {
            ByteArrayInputStream doctoredBytes = new ByteArrayInputStream(doctored.getBytes("UTF-8"));

            XMLReader reader = XMLReaderFactory.createXMLReader();
            reader.setEntityResolver(runtime.getResolver());
            SAXSource saxSource = new SAXSource(reader, new InputSource(doctoredBytes));
            DocumentBuilder builder = runtime.getProcessor().newDocumentBuilder();
            builder.setLineNumbering(true);
            builder.setDTDValidation(false);
            builder.setBaseURI(doc.getBaseURI());

            result.write(builder.build(saxSource));
        } catch (Exception e) {
            throw new XProcException(e);
        }
    }

    private Attributes attributes() {
        Attributes adAttr = new Attributes();

        String s = null;

        s = getOption(_allow_read_uri, (String) null);
        if (s != null) {
            adAttr.setAllowUriRead(bool(s));
        }

        s = getOption(_attribute_missing, (String) null);
        if (s != null) {
            adAttr.setAttributeMissing(s);
        }

        s = getOption(_attribute_undefined, (String) null);
        if (s != null) {
            adAttr.setAttributeUndefined(s);
        }

        s = getOption(_backend, (String) null);
        if (s != null) {
            adAttr.setBackend(s);
        }

        s = getOption(_title, (String) null);
        if (s != null) {
            adAttr.setTitle(s);
        }

        s = getOption(_doctype, (String) null);
        if (s != null) {
            adAttr.setDocType(s);
        }

        s = getOption(_imagesdir, (String) null);
        if (s != null) {
            adAttr.setImagesDir(s);
        }

        s = getOption(_source_language, (String) null);
        if (s != null) {
            adAttr.setSourceLanguage(s);
        }

        s = getOption(_source_highlighter, (String) null);
        if (s != null) {
            adAttr.setSourceHighlighter(s);
        }

        s = getOption(_max_include_depth, (String) null);
        if (s != null) {
            adAttr.setMaxIncludeDepth(Integer.parseInt(s));
        }

        s = getOption(_sectnumlevels, (String) null);
        if (s != null) {
            adAttr.setSectNumLevels(Integer.parseInt(s));
        }

        s = getOption(_hardbreaks, (String) null);
        if (s != null) {
            adAttr.setHardbreaks(bool(s));
        }

        s = getOption(_cache_uri, (String) null);
        if (s != null) {
            adAttr.setCacheUri(bool(s));
        }

        s = getOption(_hide_uri_scheme, (String) null);
        if (s != null) {
            adAttr.setHideUriScheme(bool(s));
        }

        s = getOption(_appendix_caption, (String) null);
        if (s != null) {
            adAttr.setAppendixCaption(s);
        }

        s = getOption(_math, (String) null);
        if (s != null) {
            adAttr.setMath(s);
        }

        s = getOption(_skip_front_matter, (String) null);
        if (s != null) {
            adAttr.setSkipFrontMatter(bool(s));
        }

        s = getOption(_setanchors, (String) null);
        if (s != null) {
            adAttr.setAnchors(bool(s));
        }

        s = getOption(_untitled_label, (String) null);
        if (s != null) {
            adAttr.setUntitledLabel(s);
        }

        s = getOption(_ignore_undefined, (String) null);
        if (s != null) {
            adAttr.setIgnoreUndefinedAttributes(bool(s));
        }

        s = getOption(_toc_placement, (String) null);
        if (s != null) {
            if ("left".equals(s)) {
                adAttr.setTableOfContents(Placement.LEFT);
            } else if ("right".equals(s)) {
                adAttr.setTableOfContents(Placement.RIGHT);
            } else if ("top".equals(s)) {
                adAttr.setTableOfContents(Placement.TOP);
            } else if ("bottom".equals(s)) {
                adAttr.setTableOfContents(Placement.BOTTOM);
            } else {
                throw new XProcException("Invalid TOC placement value: " + s);
            }
        }

        s = getOption(_toc2_placement, (String) null);
        if (s != null) {
            if ("left".equals(s)) {
                adAttr.setTableOfContents2(Placement.LEFT);
            } else if ("right".equals(s)) {
                adAttr.setTableOfContents2(Placement.RIGHT);
            } else if ("top".equals(s)) {
                adAttr.setTableOfContents2(Placement.TOP);
            } else if ("bottom".equals(s)) {
                adAttr.setTableOfContents2(Placement.BOTTOM);
            } else {
                throw new XProcException("Invalid TOC placement value: " + s);
            }
        }

        s = getOption(_showtitle, (String) null);
        if (s != null) {
            adAttr.setShowTitle(bool(s));
        }

        s = getOption(_toc, (String) null);
        if (s != null) {
            adAttr.setTableOfContents(bool(s));
        }

        s = getOption(_localdate, (String) null);
        if (s != null) {
            DateTimeFormatter parser = ISODateTimeFormat.date();
            adAttr.setLocalDate(parser.parseDateTime(s).toLocalDate().toDate());
        }

        s = getOption(_localtime, (String) null);
        if (s != null) {
            DateTimeFormatter parser = ISODateTimeFormat.timeNoMillis();
            adAttr.setLocalTime(parser.parseDateTime(s).toLocalDate().toDate());
        }

        s = getOption(_docdate, (String) null);
        if (s != null) {
            DateTimeFormatter parser = ISODateTimeFormat.date();
            adAttr.setDocDate(parser.parseDateTime(s).toLocalDate().toDate());
        }

        s = getOption(_doctime, (String) null);
        if (s != null) {
            DateTimeFormatter parser = ISODateTimeFormat.timeNoMillis();
            adAttr.setDocTime(parser.parseDateTime(s).toLocalDate().toDate());
        }

        s = getOption(_stylesheet, (String) null);
        if (s != null) {
            adAttr.setStyleSheetName(s);
        }

        s = getOption(_stylesdir, (String) null);
        if (s != null) {
            adAttr.setStylesDir(s);
        }

        s = getOption(_linkcss, (String) null);
        if (s != null) {
            adAttr.setLinkCss(bool(s));
        }

        s = getOption(_copycss, (String) null);
        if (s != null) {
            adAttr.setCopyCss(bool(s));
        }

        s = getOption(_icons, (String) null);
        if (s != null) {
            adAttr.setIcons(s);
        }

        s = getOption(_iconfont_remote, (String) null);
        if (s != null) {
            adAttr.setIconFontRemote(bool(s));
        }

        s = getOption(_iconfont_cdn, (String) null);
        if (s != null) {
            try {
                adAttr.setIconFontCdn(new URI(s));
            } catch (URISyntaxException e) {
                throw new XProcException(e);
            }
        }

        s = getOption(_iconfont_name, (String) null);
        if (s != null) {
            adAttr.setIconFontName(s);
        }

        s = getOption(_data_uri, (String) null);
        if (s != null) {
            adAttr.setDataUri(bool(s));
        }

        s = getOption(_iconsdir, (String) null);
        if (s != null) {
            adAttr.setIconsDir(s);
        }

        s = getOption(_numbered, (String) null);
        if (s != null) {
            adAttr.setSectionNumbers(bool(s));
        }

        s = getOption(_linkattrs, (String) null);
        if (s != null) {
            adAttr.setLinkAttrs(bool(s));
        }

        s = getOption(_experimental, (String) null);
        if (s != null) {
            adAttr.setExperimental(bool(s));
        }

        s = getOption(_nofooter, (String) null);
        if (s != null) {
            adAttr.setNoFooter(bool(s));
        }

        s = getOption(_compat_mode, (String) null);
        if (s != null) {
            if ("default".equals(s)) {
                adAttr.setCompatMode(CompatMode.DEFAULT);
            } else if ("legacy".equals(s)) {
                adAttr.setCompatMode(CompatMode.LEGACY);
            } else {
                throw new XProcException("Invalid compat-mode value: " + s);
            }
        }

        return adAttr;
    }

    private Options options() {
        Options adOpts = new Options();

        String s = null;
        Boolean b = null;

        s = getOption(_header_footer, (String) null);
        if (s != null) {
            adOpts.setHeaderFooter(bool(s));
        }

        s = getOption(_template_dirs, (String) null);
        if (s != null) {
            String[] dirs = s.split("\\s+");
            adOpts.setTemplateDirs(dirs);
        }

        s = getOption(_template_engine, (String) null);
        if (s != null) {
            adOpts.setTemplateEngine(s);
        }

        s = getOption(_safe, (String) null);
        if (s != null) {
            if ("safe".equals(s)) {
                adOpts.setSafe(SafeMode.SAFE);
            } else if ("unsafe".equals(s)) {
                adOpts.setSafe(SafeMode.UNSAFE);
            } else if ("server".equals(s)) {
                adOpts.setSafe(SafeMode.SERVER);
            } else if ("secure".equals(s)) {
                adOpts.setSafe(SafeMode.SECURE);
            } else {
                throw new XProcException("Invalid safe value: " + s);
            }
        }

        s = getOption(_eruby, (String) null);
        if (s != null) {
            adOpts.setEruby(s);
        }

        s = getOption(_compact, (String) null);
        if (s != null) {
            adOpts.setCompact(bool(s));
        }

        s = getOption(_base_dir, (String) null);
        if (s != null) {
            adOpts.setBaseDir(s);
        }

        s = getOption(_template_cache, (String) null);
        if (s != null) {
            adOpts.setTemplateCache(bool(s));
        }

        s = getOption(_parse_header_only, (String) null);
        if (s != null) {
            adOpts.setParseHeaderOnly(bool(s));
        }

        return adOpts;
    }

    private boolean bool(String s) {
        if ("true".equals(s)) {
            return true;
        } else if ("false".equals(s)) {
            return false;
        } else {
            throw new XProcException("Invalid boolean value: " + s);
        }
    }

    public static void configureStep(XProcRuntime runtime) {
        XProcURIResolver resolver = runtime.getResolver();
        URIResolver uriResolver = resolver.getUnderlyingURIResolver();
        URIResolver myResolver = new StepResolver(uriResolver);
        resolver.setUnderlyingURIResolver(myResolver);
    }

    private static class StepResolver implements URIResolver {
        Logger logger = LoggerFactory.getLogger(AsciiDoctor.class);
        URIResolver nextResolver = null;

        public StepResolver(URIResolver next) {
            nextResolver = next;
        }

        @Override
        public Source resolve(String href, String base) throws TransformerException {
            try {
                URI baseURI = new URI(base);
                URI xpl = baseURI.resolve(href);
                if (library_xpl.equals(xpl.toASCIIString())) {
                    URL url = AsciiDoctor.class.getResource(library_url);
                    logger.debug("Reading library.xpl for cx:asciidoctor from " + url);
                    InputStream s = AsciiDoctor.class.getResourceAsStream(library_url);
                    if (s != null) {
                        SAXSource source = new SAXSource(new InputSource(s));
                        return source;
                    } else {
                        logger.info("Failed to read " + library_url + " for cx:asciidoctor");
                    }
                }
            } catch (URISyntaxException e) {
                // nevermind
            }

            if (nextResolver != null) {
                return nextResolver.resolve(href, base);
            } else {
                return null;
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy