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

de.undercouch.citeproc.csl.internal.SCitationLayout Maven / Gradle / Ivy

package de.undercouch.citeproc.csl.internal;

import de.undercouch.citeproc.csl.CSLCitationItem;
import de.undercouch.citeproc.csl.internal.behavior.Affixes;
import de.undercouch.citeproc.csl.internal.behavior.FormattingAttributes;
import de.undercouch.citeproc.helper.NodeHelper;
import org.apache.commons.lang3.CharSet;
import org.w3c.dom.Node;

import java.util.regex.Pattern;

import static de.undercouch.citeproc.csl.internal.token.TextToken.Type.DELIMITER;
import static de.undercouch.citeproc.csl.internal.token.TextToken.Type.PREFIX;
import static de.undercouch.citeproc.csl.internal.token.TextToken.Type.SUFFIX;

/**
 * A layout element inside a citation element in a style file
 * @author Michel Kraemer
 */
public class SCitationLayout extends SRenderingElementContainerElement {
    private static final CharSet PUNCTUATION_END = CharSet.getInstance(":.,;!?)]&");
    private static final CharSet PUNCTUATION_START = CharSet.getInstance("([&");
    private static final Pattern TRIM_END = Pattern.compile("\\p{Space}+$");
    private static final Pattern IGNORE_END = Pattern.compile("[\\p{Space}\\p{Pf}\"']+$");

    private final Affixes affixes;
    private final int formattingAttributes;
    private final String delimiter;

    /**
     * Construct the layout element from an XML node
     * @param node the XML node
     */
    public SCitationLayout(Node node) {
        super(node);
        affixes = new Affixes(node);
        formattingAttributes = FormattingAttributes.of(node);
        delimiter = NodeHelper.getAttrValue(node, "delimiter");
    }

    @Override
    public void render(RenderContext ctx) {
        affixes.wrap(this::renderInternal).accept(ctx);
    }

    private void renderInternal(RenderContext ctx) {
        RenderContext tmp = new RenderContext(ctx);
        for (CSLCitationItem item : ctx.getCitation().getCitationItems()) {
            RenderContext innerTmp = new RenderContext(ctx, item);
            innerTmp.reset();
            super.render(innerTmp);

            if (delimiter != null && !tmp.getResult().isEmpty()) {
                tmp.emit(delimiter, DELIMITER);
            }

            if (item.getPrefix() != null) {
                String prefix = item.getPrefix();
                if (needsAppendSpace(prefix)) {
                    prefix = TRIM_END.matcher(prefix).replaceAll("") + " ";
                }
                tmp.emit(prefix, PREFIX);
            }

            tmp.emit(innerTmp.getResult());

            if (item.getSuffix() != null) {
                String suffix = item.getSuffix();
                if (needsPrependSpace(suffix)) {
                    suffix = " " + suffix;
                }
                tmp.emit(suffix, SUFFIX);
            }
        }
        ctx.emit(tmp.getResult(), formattingAttributes);
    }

    /**
     * Check if we need to append a space character to the given prefix
     * @param prefix the prefix
     * @return {@code true} if we need to append a space character
     */
    private static boolean needsAppendSpace(String prefix) {
        if (prefix.isEmpty()) {
            return false;
        }

        // ignore some characters at the end
        prefix = IGNORE_END.matcher(prefix).replaceAll("");

        // now check last character
        char c = prefix.charAt(prefix.length() - 1);
        return Character.isLetterOrDigit(c) || PUNCTUATION_END.contains(c);
    }

    /**
     * Check if we need to prepend a space character to the given suffix
     * @param suffix the suffix
     * @return {@code true} if we need to prepend a space character
     */
    private static boolean needsPrependSpace(String suffix) {
        if (suffix.isEmpty()) {
            return false;
        }

        char c = suffix.charAt(0);
        return Character.isLetterOrDigit(c) || PUNCTUATION_START.contains(c);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy