diff -r 7e08730da258 -r 4a1864c3e867 java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractXmlFormatter.java --- a/java/sql-dk/src/info/globalcode/sql/dk/formatting/AbstractXmlFormatter.java Mon Mar 04 17:06:42 2019 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,241 +0,0 @@ -/** - * SQL-DK - * Copyright © 2014 František Kučera (frantovo.cz) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package info.globalcode.sql.dk.formatting; - -import info.globalcode.sql.dk.ColorfulPrintWriter; -import info.globalcode.sql.dk.ColorfulPrintWriter.TerminalColor; -import java.util.Stack; -import javax.xml.namespace.QName; -import static info.globalcode.sql.dk.Functions.isEmpty; -import static info.globalcode.sql.dk.Functions.toHex; -import info.globalcode.sql.dk.configuration.PropertyDeclaration; -import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL; -import static info.globalcode.sql.dk.formatting.CommonProperties.COLORFUL_DESCRIPTION; -import java.nio.charset.Charset; -import java.util.EmptyStackException; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - *

- * Provides helper methods for printing pretty intended and optionally colorful (syntax highlighted) - * XML output. - *

- * - *

- * Must be used with care – bad usage can lead to invalid XML (e.g. using undeclared namespaces). - *

- * - * @author Ing. František Kučera (frantovo.cz) - */ -@PropertyDeclaration(name = COLORFUL, defaultValue = "false", type = Boolean.class, description = COLORFUL_DESCRIPTION) -@PropertyDeclaration(name = AbstractXmlFormatter.PROPERTY_INDENT, defaultValue = AbstractXmlFormatter.PROPERTY_INDENT_DEFAULT, type = String.class, description = "tab or sequence of spaces used for indentation of nested elements") -@PropertyDeclaration(name = AbstractXmlFormatter.PROPERTY_INDENT_TEXT, defaultValue = "true", type = Boolean.class, description = "whether text with line breaks should be indented; if not original whitespace will be preserved.") -public abstract class AbstractXmlFormatter extends AbstractFormatter { - - private static final Logger log = Logger.getLogger(AbstractXmlFormatter.class.getName()); - public static final String PROPERTY_INDENT = "indent"; - protected static final String PROPERTY_INDENT_DEFAULT = "\t"; - public static final String PROPERTY_INDENT_TEXT = "indentText"; - private static final TerminalColor ELEMENT_COLOR = TerminalColor.Magenta; - private static final TerminalColor ATTRIBUTE_NAME_COLOR = TerminalColor.Green; - private static final TerminalColor ATTRIBUTE_VALUE_COLOR = TerminalColor.Yellow; - private static final TerminalColor XML_DECLARATION_COLOR = TerminalColor.Red; - private static final TerminalColor XML_DOCTYPE_COLOR = TerminalColor.Cyan; - private Stack treePosition = new Stack<>(); - private final ColorfulPrintWriter out; - private final String indent; - private final boolean indentText; - - public AbstractXmlFormatter(FormatterContext formatterContext) { - super(formatterContext); - boolean colorful = formatterContext.getProperties().getBoolean(COLORFUL, false); - out = new ColorfulPrintWriter(formatterContext.getOutputStream(), false, colorful); - indent = formatterContext.getProperties().getString(PROPERTY_INDENT, PROPERTY_INDENT_DEFAULT); - indentText = formatterContext.getProperties().getBoolean(PROPERTY_INDENT_TEXT, true); - - if (!indent.matches("\\s*")) { - log.log(Level.WARNING, "Setting indent to „{0}“ is weird & freaky; in hex: {1}", new Object[]{indent, toHex(indent.getBytes())}); - } - - } - - protected void printStartDocument() { - out.print(XML_DECLARATION_COLOR, ""); - } - - protected void printDoctype(String doctype) { - out.print(XML_DOCTYPE_COLOR, "\n"); - } - - protected void printEndDocument() { - out.println(); - out.flush(); - if (!treePosition.empty()) { - throw new IllegalStateException("Some elements are not closed: " + treePosition); - } - } - - protected void printStartElement(QName element) { - printStartElement(element, null); - } - - protected Map singleAttribute(QName name, String value) { - Map attributes = new HashMap<>(2); - attributes.put(name, value); - return attributes; - } - - protected void printStartElement(QName element, Map attributes) { - printStartElement(element, attributes, false); - } - - /** - * @param empty whether element should be closed … /> (has no content, do not - * call {@linkplain #printEndElement()}) - */ - private void printStartElement(QName element, Map attributes, boolean empty) { - printIndent(); - - out.print(ELEMENT_COLOR, "<" + toString(element)); - - if (attributes != null) { - for (Entry attribute : attributes.entrySet()) { - out.print(" "); - out.print(ATTRIBUTE_NAME_COLOR, toString(attribute.getKey())); - out.print("="); - out.print(ATTRIBUTE_VALUE_COLOR, '"' + escapeXmlAttribute(attribute.getValue()) + '"'); - } - } - - if (empty) { - out.print(ELEMENT_COLOR, "/>"); - } else { - out.print(ELEMENT_COLOR, ">"); - treePosition.add(element); - } - - out.flush(); - } - - /** - * Prints text node wrapped in given element without indenting the text and adding line breaks - * (useful for short texts). - * - * @param attributes use {@linkplain LinkedHashMap} to preserve attributes order - */ - protected void printTextElement(QName element, Map attributes, String text) { - printStartElement(element, attributes); - - String[] lines = text.split("\\n"); - - if (indentText && lines.length > 1) { - for (String line : lines) { - printText(line, true); - } - printEndElement(true); - } else { - /* - * line breaks at the end of the text will be eaten – if you need them, use indentText = false - */ - if (lines.length == 1 && text.endsWith("\n")) { - text = text.substring(0, text.length() - 1); - } - - printText(text, false); - printEndElement(false); - } - } - - protected void printEmptyElement(QName element, Map attributes) { - printStartElement(element, attributes, true); - } - - protected void printEndElement() { - printEndElement(true); - } - - private void printEndElement(boolean indent) { - try { - QName name = treePosition.pop(); - - if (indent) { - printIndent(); - } - - out.print(ELEMENT_COLOR, ""); - out.flush(); - - } catch (EmptyStackException e) { - throw new IllegalStateException("No more elements to end.", e); - } - } - - protected void printText(String s, boolean indent) { - if (indent) { - printIndent(); - } - out.print(escapeXmlText(s)); - out.flush(); - } - - protected void printIndent() { - out.println(); - for (int i = 0; i < treePosition.size(); i++) { - out.print(indent); - } - } - - protected static QName qname(String name) { - return new QName(name); - } - - protected static QName qname(String prefix, String name) { - return new QName(null, name, prefix); - } - - private String toString(QName name) { - if (isEmpty(name.getPrefix(), true)) { - return escapeName(name.getLocalPart()); - } else { - return escapeName(name.getPrefix()) + ":" + escapeName(name.getLocalPart()); - } - } - - private String escapeName(String s) { - // TODO: avoid ugly values in - return s; - } - - private static String escapeXmlText(String s) { - return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">"); - // Not needed: - // return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'"); - } - - /** - * Expects attribute values enclosed in "quotes" not 'apostrophes'. - */ - private static String escapeXmlAttribute(String s) { - return s.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """); - } -}