
com.github.underscore.Xml Maven / Gradle / Ivy
/*
* The MIT License (MIT)
*
* Copyright 2015-2022 Valentyn Kolesnikov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.underscore;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
@SuppressWarnings({
"java:S107",
"java:S1119",
"java:S3740",
"java:S3776",
"java:S4276",
"java:S5843",
"java:S5852",
"java:S5998",
"java:S6019",
"java:S6035"
})
public final class Xml {
private Xml() {}
private static final String NULL = "null";
private static final String ELEMENT_TEXT = "element";
private static final String CDATA = "#cdata-section";
private static final String COMMENT = "#comment";
private static final String ENCODING = "#encoding";
private static final String STANDALONE = "#standalone";
private static final String OMITXMLDECLARATION = "#omit-xml-declaration";
private static final String YES = "yes";
private static final String TEXT = "#text";
private static final String NUMBER = "-number";
private static final String ELEMENT = "<" + ELEMENT_TEXT + ">";
private static final String CLOSED_ELEMENT = "" + ELEMENT_TEXT + ">";
private static final String EMPTY_ELEMENT = ELEMENT + CLOSED_ELEMENT;
private static final String NULL_TRUE = " " + NULL + "=\"true\"/>";
private static final String NUMBER_TEXT = " number=\"true\"";
private static final String NUMBER_TRUE = NUMBER_TEXT + ">";
private static final String ARRAY = "-array";
private static final String ARRAY_TRUE = " array=\"true\"";
private static final String NULL_ELEMENT = "<" + ELEMENT_TEXT + NULL_TRUE;
private static final String BOOLEAN = "-boolean";
private static final String TRUE = "true";
private static final String SELF_CLOSING = "-self-closing";
private static final String STRING = "-string";
private static final String NULL_ATTR = "-null";
private static final String EMPTY_ARRAY = "-empty-array";
private static final String QUOT = """;
private static final String XML_HEADER = "|>|\\s).)+))");
private static final Map XML_UNESCAPE = new HashMap<>();
private static final org.w3c.dom.Document DOCUMENT = Document.createDocument();
static {
XML_UNESCAPE.put(QUOT, "\"");
XML_UNESCAPE.put("&", "&");
XML_UNESCAPE.put("<", "<");
XML_UNESCAPE.put(">", ">");
XML_UNESCAPE.put("'", "'");
}
public static class XmlStringBuilder {
public enum Step {
TWO_SPACES(2),
THREE_SPACES(3),
FOUR_SPACES(4),
COMPACT(0),
TABS(1);
private int ident;
Step(int ident) {
this.ident = ident;
}
public int getIdent() {
return ident;
}
}
protected final StringBuilder builder;
private final Step identStep;
private int ident;
public XmlStringBuilder() {
builder = new StringBuilder("\n\n");
identStep = Step.TWO_SPACES;
ident = 2;
}
public XmlStringBuilder(StringBuilder builder, Step identStep, int ident) {
this.builder = builder;
this.identStep = identStep;
this.ident = ident;
}
public XmlStringBuilder append(final String string) {
builder.append(string);
return this;
}
public XmlStringBuilder fillSpaces() {
for (int index = 0; index < ident; index += 1) {
builder.append(identStep == Step.TABS ? '\t' : ' ');
}
return this;
}
public XmlStringBuilder incIdent() {
ident += identStep.getIdent();
return this;
}
public XmlStringBuilder decIdent() {
ident -= identStep.getIdent();
return this;
}
public XmlStringBuilder newLine() {
if (identStep != Step.COMPACT) {
builder.append("\n");
}
return this;
}
public int getIdent() {
return ident;
}
public Step getIdentStep() {
return identStep;
}
public String toString() {
return builder.toString() + "\n ";
}
}
public static class XmlStringBuilderWithoutRoot extends XmlStringBuilder {
public XmlStringBuilderWithoutRoot(
XmlStringBuilder.Step identStep, String encoding, String standalone) {
super(
new StringBuilder(
""
+ (identStep == Step.COMPACT ? "" : "\n")),
identStep,
0);
}
@Override
public String toString() {
return builder.toString();
}
}
public static class XmlStringBuilderWithoutHeader extends XmlStringBuilder {
public XmlStringBuilderWithoutHeader(XmlStringBuilder.Step identStep, int ident) {
super(new StringBuilder(), identStep, ident);
}
@Override
public String toString() {
return builder.toString();
}
}
public static class XmlStringBuilderText extends XmlStringBuilderWithoutHeader {
public XmlStringBuilderText(XmlStringBuilder.Step identStep, int ident) {
super(identStep, ident);
}
}
public static class XmlArray {
private XmlArray() {}
public static void writeXml(
Collection> collection,
String name,
XmlStringBuilder builder,
boolean parentTextFound,
Set namespaces,
boolean addArray) {
if (collection == null) {
builder.append(NULL);
return;
}
if (name != null) {
builder.fillSpaces().append("<").append(XmlValue.escapeName(name, namespaces));
if (addArray) {
builder.append(ARRAY_TRUE);
}
if (collection.isEmpty()) {
builder.append(" empty-array=\"true\"");
}
builder.append(">").incIdent();
if (!collection.isEmpty()) {
builder.newLine();
}
}
writeXml(collection, builder, name, parentTextFound, namespaces);
if (name != null) {
builder.decIdent();
if (!collection.isEmpty()) {
builder.newLine().fillSpaces();
}
builder.append("").append(XmlValue.escapeName(name, namespaces)).append(">");
}
}
private static void writeXml(
Collection> collection,
XmlStringBuilder builder,
String name,
final boolean parentTextFound,
Set namespaces) {
boolean localParentTextFound = parentTextFound;
final List> entries = U.newArrayList(collection);
for (int index = 0; index < entries.size(); index += 1) {
final Object value = entries.get(index);
final boolean addNewLine =
index < entries.size() - 1
&& !XmlValue.getMapKey(XmlValue.getMapValue(entries.get(index + 1)))
.startsWith(TEXT);
if (value == null) {
builder.fillSpaces()
.append(
"<"
+ (name == null
? ELEMENT_TEXT
: XmlValue.escapeName(name, namespaces))
+ (collection.size() == 1 ? ARRAY_TRUE : "")
+ NULL_TRUE);
} else {
if (value instanceof Map
&& ((Map) value).size() == 1
&& XmlValue.getMapKey(value).equals("#item")
&& XmlValue.getMapValue(value) instanceof Map) {
XmlObject.writeXml(
(Map) XmlValue.getMapValue(value),
null,
builder,
localParentTextFound,
namespaces,
true);
if (XmlValue.getMapKey(XmlValue.getMapValue(value)).startsWith(TEXT)) {
localParentTextFound = true;
continue;
}
} else {
XmlValue.writeXml(
value,
name == null ? ELEMENT_TEXT : name,
builder,
localParentTextFound,
namespaces,
collection.size() == 1 || value instanceof Collection);
}
localParentTextFound = false;
}
if (addNewLine) {
builder.newLine();
}
}
}
public static void writeXml(byte[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(short[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(int[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(long[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(float[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(double[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(boolean[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(char[] array, XmlStringBuilder builder) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
builder.fillSpaces().append(ELEMENT);
builder.append(String.valueOf(array[i]));
builder.append(CLOSED_ELEMENT);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
public static void writeXml(
Object[] array,
String name,
XmlStringBuilder builder,
boolean parentTextFound,
Set namespaces) {
if (array == null) {
builder.fillSpaces().append(NULL_ELEMENT);
} else if (array.length == 0) {
builder.fillSpaces().append(EMPTY_ELEMENT);
} else {
for (int i = 0; i < array.length; i++) {
XmlValue.writeXml(
array[i],
name == null ? ELEMENT_TEXT : name,
builder,
parentTextFound,
namespaces,
false);
if (i != array.length - 1) {
builder.newLine();
}
}
}
}
}
public static class XmlObject {
private XmlObject() {}
@SuppressWarnings("unchecked")
public static void writeXml(
final Map map,
final String name,
final XmlStringBuilder builder,
final boolean parentTextFound,
final Set namespaces,
final boolean addArray) {
if (map == null) {
XmlValue.writeXml(NULL, name, builder, false, namespaces, addArray);
return;
}
final List elems = U.newArrayList();
final List attrs = U.newArrayList();
final XmlStringBuilder.Step identStep = builder.getIdentStep();
final int ident =
builder.getIdent() + (name == null ? 0 : builder.getIdentStep().getIdent());
final List entries = U.newArrayList(map.entrySet());
final Set attrKeys = U.newLinkedHashSet();
fillNamespacesAndAttrs(map, namespaces, attrKeys);
for (int index = 0; index < entries.size(); index += 1) {
final Map.Entry entry = entries.get(index);
final boolean addNewLine =
index < entries.size() - 1
&& !String.valueOf(entries.get(index + 1).getKey())
.startsWith(TEXT);
if (String.valueOf(entry.getKey()).startsWith("-")
&& entry.getValue() instanceof String) {
attrs.add(
" "
+ XmlValue.escapeName(
String.valueOf(entry.getKey()).substring(1), namespaces)
+ "=\""
+ XmlValue.escape(String.valueOf(entry.getValue()))
.replace("\"", QUOT)
+ "\"");
} else if (String.valueOf(entry.getKey()).startsWith(TEXT)) {
addText(entry, elems, identStep, ident, attrKeys, attrs);
} else {
boolean localParentTextFound =
!elems.isEmpty()
&& elems.get(elems.size() - 1)
instanceof XmlStringBuilderText
|| parentTextFound;
processElements(
entry,
identStep,
ident,
addNewLine,
elems,
namespaces,
localParentTextFound);
}
}
if (addArray && !attrKeys.contains(ARRAY)) {
attrs.add(ARRAY_TRUE);
}
addToBuilder(name, parentTextFound, builder, namespaces, attrs, elems);
}
@SuppressWarnings("unchecked")
private static void fillNamespacesAndAttrs(
final Map map, final Set namespaces, final Set attrKeys) {
for (Map.Entry entry : (Set) map.entrySet()) {
if (String.valueOf(entry.getKey()).startsWith("-")
&& !(entry.getValue() instanceof Map)
&& !(entry.getValue() instanceof List)) {
if (String.valueOf(entry.getKey()).startsWith("-xmlns:")) {
namespaces.add(String.valueOf(entry.getKey()).substring(7));
}
attrKeys.add(String.valueOf(entry.getKey()));
}
}
}
private static void addToBuilder(
final String name,
final boolean parentTextFound,
final XmlStringBuilder builder,
final Set namespaces,
final List attrs,
final List elems) {
final boolean selfClosing = attrs.remove(" self-closing=\"true\"");
addOpenElement(name, parentTextFound, builder, namespaces, selfClosing, attrs, elems);
if (!selfClosing) {
for (XmlStringBuilder localBuilder1 : elems) {
builder.append(localBuilder1.toString());
}
}
if (name != null) {
builder.decIdent();
if (!elems.isEmpty()
&& !(elems.get(elems.size() - 1) instanceof XmlStringBuilderText)) {
builder.newLine().fillSpaces();
}
if (!selfClosing) {
builder.append("").append(XmlValue.escapeName(name, namespaces)).append(">");
}
}
}
private static void addOpenElement(
final String name,
final boolean parentTextFound,
final XmlStringBuilder builder,
final Set namespaces,
final boolean selfClosing,
final List attrs,
final List elems) {
if (name != null) {
if (!parentTextFound) {
builder.fillSpaces();
}
builder.append("<")
.append(XmlValue.escapeName(name, namespaces))
.append(U.join(attrs, ""));
if (selfClosing) {
builder.append("/");
}
builder.append(">").incIdent();
if (!elems.isEmpty() && !(elems.get(0) instanceof XmlStringBuilderText)) {
builder.newLine();
}
}
}
private static void processElements(
final Map.Entry entry,
final XmlStringBuilder.Step identStep,
final int ident,
final boolean addNewLine,
final List elems,
final Set namespaces,
final boolean parentTextFound) {
if (String.valueOf(entry.getKey()).startsWith(COMMENT)) {
addComment(entry, identStep, ident, parentTextFound, addNewLine, elems);
} else if (String.valueOf(entry.getKey()).startsWith(CDATA)) {
addCdata(entry, identStep, ident, addNewLine, elems);
} else if (entry.getValue() instanceof List && !((List) entry.getValue()).isEmpty()) {
addElements(identStep, ident, entry, namespaces, elems, addNewLine);
} else {
addElement(identStep, ident, entry, namespaces, elems, addNewLine);
}
}
private static void addText(
final Map.Entry entry,
final List elems,
final XmlStringBuilder.Step identStep,
final int ident,
final Set attrKeys,
final List attrs) {
if (entry.getValue() instanceof List) {
for (Object value : (List) entry.getValue()) {
elems.add(
new XmlStringBuilderText(identStep, ident)
.append(XmlValue.escape(String.valueOf(value))));
}
} else {
if (entry.getValue() instanceof Number && !attrKeys.contains(NUMBER)) {
attrs.add(NUMBER_TEXT);
} else if (entry.getValue() instanceof Boolean && !attrKeys.contains(BOOLEAN)) {
attrs.add(" boolean=\"true\"");
} else if (entry.getValue() == null && !attrKeys.contains(NULL_ATTR)) {
attrs.add(" null=\"true\"");
return;
} else if ("".equals(entry.getValue()) && !attrKeys.contains(STRING)) {
attrs.add(" string=\"true\"");
return;
}
elems.add(
new XmlStringBuilderText(identStep, ident)
.append(XmlValue.escape(String.valueOf(entry.getValue()))));
}
}
private static void addElements(
final XmlStringBuilder.Step identStep,
final int ident,
Map.Entry entry,
Set namespaces,
final List elems,
final boolean addNewLine) {
boolean parentTextFound =
!elems.isEmpty() && elems.get(elems.size() - 1) instanceof XmlStringBuilderText;
final XmlStringBuilder localBuilder =
new XmlStringBuilderWithoutHeader(identStep, ident);
XmlArray.writeXml(
(List) entry.getValue(),
localBuilder,
String.valueOf(entry.getKey()),
parentTextFound,
namespaces);
if (addNewLine) {
localBuilder.newLine();
}
elems.add(localBuilder);
}
private static void addElement(
final XmlStringBuilder.Step identStep,
final int ident,
Map.Entry entry,
Set namespaces,
final List elems,
final boolean addNewLine) {
boolean parentTextFound =
!elems.isEmpty() && elems.get(elems.size() - 1) instanceof XmlStringBuilderText;
XmlStringBuilder localBuilder = new XmlStringBuilderWithoutHeader(identStep, ident);
XmlValue.writeXml(
entry.getValue(),
String.valueOf(entry.getKey()),
localBuilder,
parentTextFound,
namespaces,
false);
if (addNewLine) {
localBuilder.newLine();
}
elems.add(localBuilder);
}
private static void addComment(
Map.Entry entry,
XmlStringBuilder.Step identStep,
int ident,
boolean parentTextFound,
boolean addNewLine,
List elems) {
if (entry.getValue() instanceof List) {
for (Iterator iterator = ((List) entry.getValue()).iterator();
iterator.hasNext(); ) {
elems.add(
addCommentValue(
identStep,
ident,
String.valueOf(iterator.next()),
parentTextFound,
iterator.hasNext() || addNewLine));
}
} else {
elems.add(
addCommentValue(
identStep,
ident,
String.valueOf(entry.getValue()),
parentTextFound,
addNewLine));
}
}
private static XmlStringBuilder addCommentValue(
XmlStringBuilder.Step identStep,
int ident,
String value,
boolean parentTextFound,
boolean addNewLine) {
XmlStringBuilder localBuilder = new XmlStringBuilderWithoutHeader(identStep, ident);
if (!parentTextFound) {
localBuilder.fillSpaces();
}
localBuilder.append("");
if (addNewLine) {
localBuilder.newLine();
}
return localBuilder;
}
private static void addCdata(
Map.Entry entry,
XmlStringBuilder.Step identStep,
int ident,
boolean addNewLine,
List elems) {
if (entry.getValue() instanceof List) {
for (Iterator iterator = ((List) entry.getValue()).iterator();
iterator.hasNext(); ) {
elems.add(
addCdataValue(
identStep,
ident,
String.valueOf(iterator.next()),
iterator.hasNext() || addNewLine));
}
} else {
elems.add(
addCdataValue(
identStep, ident, String.valueOf(entry.getValue()), addNewLine));
}
}
private static XmlStringBuilder addCdataValue(
XmlStringBuilder.Step identStep, int ident, String value, boolean addNewLine) {
XmlStringBuilder localBuilder = new XmlStringBuilderText(identStep, ident);
localBuilder.append("");
if (addNewLine) {
localBuilder.newLine();
}
return localBuilder;
}
}
public static class XmlValue {
private XmlValue() {}
public static void writeXml(
Object value,
String name,
XmlStringBuilder builder,
boolean parentTextFound,
Set namespaces,
boolean addArray) {
if (value instanceof Map) {
XmlObject.writeXml(
(Map) value, name, builder, parentTextFound, namespaces, addArray);
return;
}
if (value instanceof Collection) {
XmlArray.writeXml(
(Collection) value, name, builder, parentTextFound, namespaces, addArray);
return;
}
if (!parentTextFound) {
builder.fillSpaces();
}
if (value == null) {
builder.append("<" + XmlValue.escapeName(name, namespaces) + NULL_TRUE);
} else if (value instanceof String) {
if (((String) value).isEmpty()) {
builder.append(
"<"
+ XmlValue.escapeName(name, namespaces)
+ (addArray ? ARRAY_TRUE : ""));
if (name.startsWith("?")) {
builder.append("?>");
} else {
builder.append(" string=\"true\"/>");
}
} else {
builder.append(
"<"
+ XmlValue.escapeName(name, namespaces)
+ (addArray ? ARRAY_TRUE : "")
+ (name.startsWith("?") ? " " : ">"));
builder.append(escape((String) value));
if (name.startsWith("?")) {
builder.append("?>");
} else {
builder.append("" + XmlValue.escapeName(name, namespaces) + ">");
}
}
} else {
processArrays(value, builder, name, parentTextFound, namespaces, addArray);
}
}
private static void processArrays(
Object value,
XmlStringBuilder builder,
String name,
boolean parentTextFound,
Set namespaces,
boolean addArray) {
if (value instanceof Double) {
if (((Double) value).isInfinite() || ((Double) value).isNaN()) {
builder.append(NULL_ELEMENT);
} else {
builder.append(
"<"
+ XmlValue.escapeName(name, namespaces)
+ (addArray ? ARRAY_TRUE : "")
+ NUMBER_TRUE);
builder.append(value.toString());
builder.append("" + XmlValue.escapeName(name, namespaces) + ">");
}
} else if (value instanceof Float) {
if (((Float) value).isInfinite() || ((Float) value).isNaN()) {
builder.append(NULL_ELEMENT);
} else {
builder.append("<" + XmlValue.escapeName(name, namespaces) + NUMBER_TRUE);
builder.append(value.toString());
builder.append("" + XmlValue.escapeName(name, namespaces) + ">");
}
} else if (value instanceof Number) {
builder.append(
"<"
+ XmlValue.escapeName(name, namespaces)
+ (addArray ? ARRAY_TRUE : "")
+ NUMBER_TRUE);
builder.append(value.toString());
builder.append("" + XmlValue.escapeName(name, namespaces) + ">");
} else if (value instanceof Boolean) {
builder.append(
"<"
+ XmlValue.escapeName(name, namespaces)
+ (addArray ? ARRAY_TRUE : "")
+ " boolean=\"true\">");
builder.append(value.toString());
builder.append("" + XmlValue.escapeName(name, namespaces) + ">");
} else {
builder.append("<" + XmlValue.escapeName(name, namespaces) + ">");
if (value instanceof byte[]) {
builder.newLine().incIdent();
XmlArray.writeXml((byte[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else if (value instanceof short[]) {
builder.newLine().incIdent();
XmlArray.writeXml((short[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else {
processArrays2(value, builder, name, parentTextFound, namespaces);
}
builder.append("" + XmlValue.escapeName(name, namespaces) + ">");
}
}
private static void processArrays2(
Object value,
XmlStringBuilder builder,
String name,
boolean parentTextFound,
Set namespaces) {
if (value instanceof int[]) {
builder.newLine().incIdent();
XmlArray.writeXml((int[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else if (value instanceof long[]) {
builder.newLine().incIdent();
XmlArray.writeXml((long[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else if (value instanceof float[]) {
builder.newLine().incIdent();
XmlArray.writeXml((float[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else if (value instanceof double[]) {
builder.newLine().incIdent();
XmlArray.writeXml((double[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else if (value instanceof boolean[]) {
builder.newLine().incIdent();
XmlArray.writeXml((boolean[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else if (value instanceof char[]) {
builder.newLine().incIdent();
XmlArray.writeXml((char[]) value, builder);
builder.decIdent().newLine().fillSpaces();
} else if (value instanceof Object[]) {
builder.newLine().incIdent();
XmlArray.writeXml((Object[]) value, name, builder, parentTextFound, namespaces);
builder.decIdent().newLine().fillSpaces();
} else {
builder.append(value.toString());
}
}
public static String escapeName(String name, Set namespaces) {
final int length = name.length();
if (length == 0) {
return "__EE__EMPTY__EE__";
}
final StringBuilder result = new StringBuilder();
char ch = name.charAt(0);
if (ch != ':') {
try {
if (ch != '?') {
DOCUMENT.createElement(String.valueOf(ch));
}
result.append(ch);
} catch (Exception ex) {
result.append("__").append(Base32.encode(Character.toString(ch))).append("__");
}
} else {
result.append("__").append(Base32.encode(Character.toString(ch))).append("__");
}
for (int i = 1; i < length; ++i) {
ch = name.charAt(i);
if (ch == ':'
&& ("xmlns".equals(name.substring(0, i))
|| namespaces.contains(name.substring(0, i)))) {
result.append(ch);
} else if (ch != ':') {
try {
DOCUMENT.createElement("a" + ch);
result.append(ch);
} catch (Exception ex) {
result.append("__")
.append(Base32.encode(Character.toString(ch)))
.append("__");
}
} else {
result.append("__").append(Base32.encode(Character.toString(ch))).append("__");
}
}
return result.toString();
}
public static String escape(String s) {
if (s == null) {
return "";
}
StringBuilder sb = new StringBuilder();
escape(s, sb);
return sb.toString();
}
private static void escape(String s, StringBuilder sb) {
final int len = s.length();
for (int i = 0; i < len; i++) {
char ch = s.charAt(i);
switch (ch) {
case '\'':
sb.append("'");
break;
case '&':
sb.append("&");
break;
case '<':
sb.append("<");
break;
case '>':
sb.append(">");
break;
case '\b':
sb.append("\\b");
break;
case '\f':
sb.append("\\f");
break;
case '\n':
sb.append("\n");
break;
case '\r':
sb.append("
");
break;
case '\t':
sb.append("\t");
break;
case '€':
sb.append("€");
break;
default:
if (ch <= '\u001F'
|| ch >= '\u007F' && ch <= '\u009F'
|| ch >= '\u2000' && ch <= '\u20FF') {
String ss = Integer.toHexString(ch);
sb.append("");
for (int k = 0; k < 4 - ss.length(); k++) {
sb.append('0');
}
sb.append(ss.toUpperCase()).append(";");
} else {
sb.append(ch);
}
break;
}
}
}
public static String unescape(String s) {
if (s == null) {
return "";
}
StringBuilder sb = new StringBuilder();
unescape(s, sb);
return sb.toString();
}
private static void unescape(String s, StringBuilder sb) {
final int len = s.length();
final StringBuilder localSb = new StringBuilder();
int index = 0;
while (index < len) {
final int skipChars = translate(s, index, localSb);
if (skipChars > 0) {
sb.append(localSb);
localSb.setLength(0);
index += skipChars;
} else {
sb.append(s.charAt(index));
index += 1;
}
}
}
private static int translate(
final CharSequence input, final int index, final StringBuilder builder) {
final int shortest = 4;
final int longest = 6;
if ('&' == input.charAt(index)) {
int max = longest;
if (index + longest > input.length()) {
max = input.length() - index;
}
for (int i = max; i >= shortest; i--) {
final CharSequence subSeq = input.subSequence(index, index + i);
final String result = XML_UNESCAPE.get(subSeq.toString());
if (result != null) {
builder.append(result);
return i;
}
}
}
return 0;
}
public static String getMapKey(Object map) {
return map instanceof Map && !((Map) map).isEmpty()
? String.valueOf(
((Map.Entry) ((Map) map).entrySet().iterator().next()).getKey())
: "";
}
public static Object getMapValue(Object map) {
return map instanceof Map && !((Map) map).isEmpty()
? ((Map.Entry) ((Map) map).entrySet().iterator().next()).getValue()
: null;
}
}
public static String toXml(Collection collection, XmlStringBuilder.Step identStep) {
final XmlStringBuilder builder =
new XmlStringBuilderWithoutRoot(identStep, UTF_8.name(), "");
writeArray(collection, builder);
return builder.toString();
}
public static String toXml(Collection collection) {
return toXml(collection, XmlStringBuilder.Step.TWO_SPACES);
}
public static String toXml(Map map, XmlStringBuilder.Step identStep) {
return toXml(map, identStep, ROOT);
}
public static String toXml(Map map, XmlStringBuilder.Step identStep, String newRootName) {
final XmlStringBuilder builder;
final Map localMap;
if (map != null && map.containsKey(ENCODING)) {
localMap = (Map) ((LinkedHashMap) map).clone();
builder =
checkStandalone(String.valueOf(localMap.remove(ENCODING)), identStep, localMap);
} else if (map != null && map.containsKey(STANDALONE)) {
localMap = (Map) ((LinkedHashMap) map).clone();
builder =
new XmlStringBuilderWithoutRoot(
identStep,
UTF_8.name(),
" standalone=\""
+ (YES.equals(map.get(STANDALONE)) ? YES : "no")
+ "\"");
localMap.remove(STANDALONE);
} else if (map != null && map.containsKey(OMITXMLDECLARATION)) {
localMap = (Map) ((LinkedHashMap) map).clone();
builder = new XmlStringBuilderWithoutHeader(identStep, 0);
localMap.remove(OMITXMLDECLARATION);
} else {
builder = new XmlStringBuilderWithoutRoot(identStep, UTF_8.name(), "");
localMap = map;
}
checkLocalMap(builder, localMap, newRootName);
return builder.toString();
}
private static void checkLocalMap(
final XmlStringBuilder builder, final Map localMap, final String newRootName) {
final Map localMap2;
if (localMap != null && localMap.containsKey(DOCTYPE_TEXT)) {
localMap2 = (Map) ((LinkedHashMap) localMap).clone();
localMap2.remove(DOCTYPE_TEXT);
builder.append(DOCTYPE_HEADER)
.append(String.valueOf(localMap.get(DOCTYPE_TEXT)))
.append(">")
.newLine();
} else {
localMap2 = localMap;
}
if (localMap2 == null
|| localMap2.size() != 1
|| XmlValue.getMapKey(localMap2).startsWith("-")
|| XmlValue.getMapValue(localMap2) instanceof List) {
if ("root".equals(XmlValue.getMapKey(localMap2))) {
writeArray((List) XmlValue.getMapValue(localMap2), builder);
} else {
XmlObject.writeXml(
localMap2,
getRootName(localMap2, newRootName),
builder,
false,
U.newLinkedHashSet(),
false);
}
} else {
XmlObject.writeXml(
localMap2,
getRootName(localMap2, newRootName),
builder,
false,
U.newLinkedHashSet(),
false);
}
}
private static void writeArray(final Collection collection, final XmlStringBuilder builder) {
builder.append("").incIdent();
if (collection != null && !collection.isEmpty()) {
builder.newLine();
}
XmlArray.writeXml(collection, null, builder, false, U.newLinkedHashSet(), false);
if (collection != null && !collection.isEmpty()) {
builder.newLine();
}
builder.append(" ");
}
private static XmlStringBuilder checkStandalone(
String encoding, XmlStringBuilder.Step identStep, final Map localMap) {
final XmlStringBuilder builder;
if (localMap.containsKey(STANDALONE)) {
builder =
new XmlStringBuilderWithoutRoot(
identStep,
encoding,
" standalone=\""
+ (YES.equals(localMap.get(STANDALONE)) ? YES : "no")
+ "\"");
localMap.remove(STANDALONE);
} else {
builder = new XmlStringBuilderWithoutRoot(identStep, encoding, "");
}
return builder;
}
@SuppressWarnings("unchecked")
private static String getRootName(final Map localMap, final String newRootName) {
int foundAttrs = 0;
int foundElements = 0;
int foundListElements = 0;
if (localMap != null) {
for (Map.Entry entry : (Set) localMap.entrySet()) {
if (String.valueOf(entry.getKey()).startsWith("-")) {
foundAttrs += 1;
} else if (!String.valueOf(entry.getKey()).startsWith(COMMENT)
&& !String.valueOf(entry.getKey()).startsWith(CDATA)
&& !String.valueOf(entry.getKey()).startsWith("?")) {
if (entry.getValue() instanceof List && ((List) entry.getValue()).size() > 1) {
foundListElements += 1;
}
foundElements += 1;
}
}
}
return foundAttrs == 0 && foundElements == 1 && foundListElements == 0 ? null : newRootName;
}
public static String toXml(Map map) {
return toXml(map, XmlStringBuilder.Step.TWO_SPACES, ROOT);
}
@SuppressWarnings("unchecked")
private static Object getValue(final String name, final Object value, final FromType fromType) {
final Object localValue;
if (value instanceof Map && ((Map) value).entrySet().size() == 1) {
final Map.Entry entry =
((Map) value).entrySet().iterator().next();
if (TEXT.equals(entry.getKey())
|| (fromType == FromType.FOR_CONVERT && ELEMENT_TEXT.equals(entry.getKey()))) {
localValue = entry.getValue();
} else {
localValue = value;
}
} else {
localValue = value;
}
return localValue instanceof String && name.startsWith("-")
? XmlValue.unescape((String) localValue)
: localValue;
}
public static Object stringToNumber(String number) {
final Object localValue;
if (number.contains(".") || number.contains("e") || number.contains("E")) {
if (number.length() > 9
|| (number.contains(".") && number.length() - number.lastIndexOf('.') > 2)
&& number.charAt(number.length() - 1) == '0') {
localValue = new java.math.BigDecimal(number);
} else {
localValue = Double.valueOf(number);
}
} else {
if (number.length() > 19) {
localValue = new java.math.BigInteger(number);
} else {
localValue = Long.valueOf(number);
}
}
return localValue;
}
private static Object createMap(
final org.w3c.dom.Node node,
final BiFunction
© 2015 - 2025 Weber Informatics LLC | Privacy Policy