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

com.github.bingoohuang.utils.xml.PrettyPrintHandler Maven / Gradle / Ivy

package com.github.bingoohuang.utils.xml;

import javax.xml.stream.XMLStreamWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import static com.github.bingoohuang.utils.xml.Xmls.MarshalOption.PrettyFormat;
import static com.github.bingoohuang.utils.xml.Xmls.MarshalOption.WithXmlDeclaration;
import static org.apache.commons.lang3.StringUtils.repeat;

public class PrettyPrintHandler implements InvocationHandler {
    private final XMLStreamWriter target;
    private final boolean prettyFormat;
    private final boolean withXmlDeclaration;
    private int depth = 0;
    private final Map hasChildElement = new HashMap();
    private static final String INDENT_CHAR = "  ";
    private static final String LINEFEED_CHAR = "\n";
    private static final Pattern XML_CHARS = Pattern.compile("[&<>]");

    public PrettyPrintHandler(XMLStreamWriter target, EnumSet marshalOptions) {
        this.target = target;
        this.prettyFormat = marshalOptions.contains(PrettyFormat);
        this.withXmlDeclaration = marshalOptions.contains(WithXmlDeclaration);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        if ("writeStartDocument".equals(methodName)) {
            if (withXmlDeclaration) target.writeStartDocument("UTF-8", "1.0");
            return null;
        }

        if ("writeCharacters".equals(methodName) && args[0] instanceof String) {
            String text = (String) args[0];
            boolean useCData = XML_CHARS.matcher(text).find();
            if (useCData) target.writeCData(text);
            else target.writeCharacters(text);
            return null;
        }

        // Needs to be BEFORE the actual event, so that for instance the
        // sequence writeStartElem, writeAttr, writeStartElem, writeEndElem, writeEndElem
        // is correctly handled
        if ("writeStartElement".equals(methodName) && prettyFormat) {
            // update state of parent node
            if (depth > 0) hasChildElement.put(depth - 1, true);
            // reset state of current node
            hasChildElement.put(depth, false);
            // indent for current depth
            if (depth > 0 || withXmlDeclaration) target.writeCharacters(LINEFEED_CHAR);
            target.writeCharacters(repeat(INDENT_CHAR, depth));
            depth++;
        } else if ("writeEndElement".equals(methodName) && prettyFormat) {
            depth--;
            if (hasChildElement.get(depth) == true) {
                target.writeCharacters(LINEFEED_CHAR);
                target.writeCharacters(repeat(INDENT_CHAR, depth));
            }
        }

        return method.invoke(target, args);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy