XMLSymbolPrinter.java

package com.jeantessier.classreader;

import java.io.*;
import java.util.Map;
import java.util.stream.*;

public class XMLSymbolPrinter extends SymbolPrinter {
    public static final String DEFAULT_ENCODING   = "utf-8";
    public static final String DEFAULT_DTD_PREFIX = "https://jeantessier.github.io/dependency-finder/dtd";
    public static final String DEFAULT_INDENT_TEXT = "    ";

    private final String encoding;
    private final String dtdPrefix;
    private final String indentText;

    private int indentLevel = 0;

    public XMLSymbolPrinter(PrintWriter out, String encoding, String dtdPrefix, String indentText) {
        super(out);
        this.encoding = encoding;
        this.dtdPrefix = dtdPrefix;
        this.indentText = indentText;
    }

    public void print(SymbolGatherer gatherer) throws IOException {
        printHeader(encoding, dtdPrefix);

        indent().append("<symbols>").eol();
        raiseIndent();

        gatherer.stream()
                .map(this::visitableToRecord)
                .forEach(this::printRecord);

        lowerIndent();
        indent().append("</symbols>").eol();
    }

    private void printHeader(String encoding, String dtdPrefix) {
        append("<?xml version=\"1.0\" encoding=\"").append(encoding).append("\" ?>").eol();
        eol();
        append("<!DOCTYPE symbols SYSTEM \"").append(dtdPrefix).append("/symbols.dtd\">").eol();
        eol();
    }

    private void printRecord(Map<String, ?> record) {
        var nodeName = record.get("symbol type").toString().replaceAll("\\s+", "-");

        indent().append("<").append(nodeName).append(">").eol();
        raiseIndent();

        record.entrySet().stream()
                .filter(entry -> !entry.getKey().equals("symbol type"))
                .forEach(entry -> {
                    var key = entry.getKey().replaceAll("\\s+", "-");

                    if (entry.getValue() instanceof Stream<?> stream) {
                        indent().append("<parameter-types>").eol();
                        raiseIndent();

                        stream.forEach(parameterType -> {
                            indent().append("<parameter-type>").append(parameterType).append("</parameter-type>").eol();
                        });

                        lowerIndent();
                        indent().append("</parameter-types>").eol();
                    } else {
                        indent().append("<").append(key).append(">").append(entry.getValue()).append("</").append(key).append(">").eol();
                    }
                });

        lowerIndent();
        indent().append("</").append(nodeName).append(">").eol();
    }

    private XMLSymbolPrinter append(Object obj) {
        getOut().print(obj);
        return this;
    }

    private XMLSymbolPrinter append(String s) {
        getOut().print(s);
        return this;
    }

    private XMLSymbolPrinter indent() {
        IntStream.rangeClosed(1, indentLevel).forEach(i -> append(indentText));
        return this;
    }

    private void eol() {
        getOut().println();
    }

    private void raiseIndent() {
        indentLevel++;
    }

    private void lowerIndent() {
        indentLevel--;
    }
}