2014-03-20 14:35:38 +01:00
|
|
|
/**
|
|
|
|
*
|
2020-09-17 12:31:35 +02:00
|
|
|
* Copyright 2014-2020 Florian Schmaus
|
2014-03-20 14:35:38 +01:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
package org.jivesoftware.smack.util;
|
|
|
|
|
2015-05-21 20:10:31 +02:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.Writer;
|
2019-09-21 22:56:20 +02:00
|
|
|
import java.util.ArrayList;
|
2015-01-01 23:57:44 +01:00
|
|
|
import java.util.Collection;
|
2015-03-30 18:24:56 +02:00
|
|
|
import java.util.Date;
|
2019-09-21 22:56:20 +02:00
|
|
|
import java.util.List;
|
2015-01-01 23:57:44 +01:00
|
|
|
|
2014-07-05 11:58:13 +02:00
|
|
|
import org.jivesoftware.smack.packet.Element;
|
2015-02-26 18:41:17 +01:00
|
|
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
2019-02-04 08:59:39 +01:00
|
|
|
import org.jivesoftware.smack.packet.FullyQualifiedElement;
|
2017-06-14 17:12:43 +02:00
|
|
|
import org.jivesoftware.smack.packet.NamedElement;
|
2019-02-04 13:27:41 +01:00
|
|
|
import org.jivesoftware.smack.packet.XmlEnvironment;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2015-03-30 18:24:56 +02:00
|
|
|
import org.jxmpp.util.XmppDateTime;
|
2014-03-21 09:51:52 +01:00
|
|
|
|
2018-04-25 14:20:18 +02:00
|
|
|
public class XmlStringBuilder implements Appendable, CharSequence, Element {
|
2014-08-20 21:25:14 +02:00
|
|
|
public static final String RIGHT_ANGLE_BRACKET = Character.toString('>');
|
2014-03-20 14:35:38 +01:00
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
private final LazyStringBuilder sb;
|
2014-03-20 14:35:38 +01:00
|
|
|
|
2019-02-04 13:27:41 +01:00
|
|
|
private final XmlEnvironment effectiveXmlEnvironment;
|
2018-04-25 14:20:18 +02:00
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
public XmlStringBuilder() {
|
2014-03-21 09:51:52 +01:00
|
|
|
sb = new LazyStringBuilder();
|
2019-09-07 18:17:08 +02:00
|
|
|
effectiveXmlEnvironment = null;
|
2014-03-21 09:51:52 +01:00
|
|
|
}
|
|
|
|
|
2015-02-26 18:41:17 +01:00
|
|
|
public XmlStringBuilder(ExtensionElement pe) {
|
2019-09-07 18:17:08 +02:00
|
|
|
this(pe, null);
|
2014-03-20 14:35:38 +01:00
|
|
|
}
|
|
|
|
|
2014-09-11 09:49:16 +02:00
|
|
|
public XmlStringBuilder(NamedElement e) {
|
2014-07-05 11:58:13 +02:00
|
|
|
this();
|
|
|
|
halfOpenElement(e.getElementName());
|
|
|
|
}
|
|
|
|
|
2019-09-04 09:48:02 +02:00
|
|
|
public XmlStringBuilder(FullyQualifiedElement element, XmlEnvironment enclosingXmlEnvironment) {
|
|
|
|
sb = new LazyStringBuilder();
|
|
|
|
halfOpenElement(element);
|
2019-09-07 18:17:08 +02:00
|
|
|
|
|
|
|
String xmlNs = element.getNamespace();
|
|
|
|
String xmlLang = element.getLanguage();
|
|
|
|
if (enclosingXmlEnvironment == null) {
|
|
|
|
xmlnsAttribute(xmlNs);
|
|
|
|
xmllangAttribute(xmlLang);
|
|
|
|
} else {
|
|
|
|
if (!enclosingXmlEnvironment.effectiveNamespaceEquals(xmlNs)) {
|
|
|
|
xmlnsAttribute(xmlNs);
|
|
|
|
}
|
|
|
|
if (!enclosingXmlEnvironment.effectiveLanguageEquals(xmlLang)) {
|
|
|
|
xmllangAttribute(xmlLang);
|
|
|
|
}
|
2019-09-04 09:48:02 +02:00
|
|
|
}
|
2019-09-07 18:17:08 +02:00
|
|
|
|
2019-09-04 09:48:02 +02:00
|
|
|
effectiveXmlEnvironment = XmlEnvironment.builder()
|
2019-09-07 18:17:08 +02:00
|
|
|
.withNamespace(xmlNs)
|
|
|
|
.withLanguage(xmlLang)
|
2019-09-04 09:48:02 +02:00
|
|
|
.withNext(enclosingXmlEnvironment)
|
|
|
|
.build();
|
2015-10-13 22:31:11 +02:00
|
|
|
}
|
|
|
|
|
2020-05-13 20:14:41 +02:00
|
|
|
public XmlEnvironment getXmlEnvironment() {
|
|
|
|
return effectiveXmlEnvironment;
|
|
|
|
}
|
|
|
|
|
2014-11-07 21:12:01 +01:00
|
|
|
public XmlStringBuilder escapedElement(String name, String escapedContent) {
|
|
|
|
assert escapedContent != null;
|
|
|
|
openElement(name);
|
|
|
|
append(escapedContent);
|
|
|
|
closeElement(name);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Add a new element to this builder.
|
2014-03-20 14:35:38 +01:00
|
|
|
*
|
2019-08-30 12:08:30 +02:00
|
|
|
* @param name TODO javadoc me please
|
|
|
|
* @param content TODO javadoc me please
|
2014-03-21 09:51:52 +01:00
|
|
|
* @return the XmlStringBuilder
|
2014-03-20 14:35:38 +01:00
|
|
|
*/
|
|
|
|
public XmlStringBuilder element(String name, String content) {
|
2018-06-07 17:15:16 +02:00
|
|
|
if (content.isEmpty()) {
|
|
|
|
return emptyElement(name);
|
|
|
|
}
|
2014-03-20 14:35:38 +01:00
|
|
|
openElement(name);
|
|
|
|
escape(content);
|
|
|
|
closeElement(name);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
/**
|
2015-03-30 18:24:56 +02:00
|
|
|
* Add a new element to this builder, with the {@link java.util.Date} instance as its content,
|
2017-12-13 23:10:11 +01:00
|
|
|
* which will get formatted with {@link XmppDateTime#formatXEP0082Date(Date)}.
|
2015-03-30 18:24:56 +02:00
|
|
|
*
|
|
|
|
* @param name element name
|
|
|
|
* @param content content of element
|
|
|
|
* @return this XmlStringBuilder
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder element(String name, Date content) {
|
|
|
|
assert content != null;
|
|
|
|
return element(name, XmppDateTime.formatXEP0082Date(content));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Add a new element to this builder.
|
2015-02-14 17:15:02 +01:00
|
|
|
*
|
2019-08-30 12:08:30 +02:00
|
|
|
* @param name TODO javadoc me please
|
|
|
|
* @param content TODO javadoc me please
|
2015-02-14 17:15:02 +01:00
|
|
|
* @return the XmlStringBuilder
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder element(String name, CharSequence content) {
|
|
|
|
return element(name, content.toString());
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
public XmlStringBuilder element(String name, Enum<?> content) {
|
2014-03-21 09:51:52 +01:00
|
|
|
assert content != null;
|
2019-08-03 22:15:44 +02:00
|
|
|
element(name, content.toString());
|
2014-03-21 09:51:52 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-09-07 18:17:08 +02:00
|
|
|
/**
|
|
|
|
* Deprecated.
|
|
|
|
*
|
|
|
|
* @param element deprecated.
|
|
|
|
* @return deprecated.
|
|
|
|
* @deprecated use {@link #append(Element)} instead.
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
// TODO: Remove in Smack 4.5.
|
2014-09-05 00:18:48 +02:00
|
|
|
public XmlStringBuilder element(Element element) {
|
|
|
|
assert element != null;
|
2019-02-04 13:27:41 +01:00
|
|
|
return append(element.toXML());
|
2014-09-05 00:18:48 +02:00
|
|
|
}
|
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
public XmlStringBuilder optElement(String name, String content) {
|
|
|
|
if (content != null) {
|
|
|
|
element(name, content);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-03-30 18:24:56 +02:00
|
|
|
/**
|
|
|
|
* Add a new element to this builder, with the {@link java.util.Date} instance as its content,
|
2017-12-13 23:10:11 +01:00
|
|
|
* which will get formatted with {@link XmppDateTime#formatXEP0082Date(Date)}
|
2015-03-30 18:24:56 +02:00
|
|
|
* if {@link java.util.Date} instance is not <code>null</code>.
|
|
|
|
*
|
|
|
|
* @param name element name
|
|
|
|
* @param content content of element
|
|
|
|
* @return this XmlStringBuilder
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder optElement(String name, Date content) {
|
|
|
|
if (content != null) {
|
|
|
|
element(name, content);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
public XmlStringBuilder optElement(String name, CharSequence content) {
|
|
|
|
if (content != null) {
|
|
|
|
element(name, content.toString());
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-07-14 19:22:02 +02:00
|
|
|
public XmlStringBuilder optElement(Element element) {
|
|
|
|
if (element != null) {
|
2019-09-07 18:17:08 +02:00
|
|
|
append(element);
|
2014-07-14 19:22:02 +02:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
public XmlStringBuilder optElement(String name, Enum<?> content) {
|
2014-03-20 14:35:38 +01:00
|
|
|
if (content != null) {
|
2014-03-21 09:51:52 +01:00
|
|
|
element(name, content);
|
2014-03-20 14:35:38 +01:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
2015-03-17 11:33:02 +01:00
|
|
|
|
2015-03-13 07:08:21 +01:00
|
|
|
public XmlStringBuilder optElement(String name, Object object) {
|
|
|
|
if (object != null) {
|
|
|
|
element(name, object.toString());
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
2014-03-20 14:35:38 +01:00
|
|
|
|
2014-09-13 11:03:40 +02:00
|
|
|
public XmlStringBuilder optIntElement(String name, int value) {
|
|
|
|
if (value >= 0) {
|
|
|
|
element(name, String.valueOf(value));
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
public XmlStringBuilder halfOpenElement(String name) {
|
2019-07-24 09:18:39 +02:00
|
|
|
assert StringUtils.isNotEmpty(name);
|
2014-03-20 14:35:38 +01:00
|
|
|
sb.append('<').append(name);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-10-11 17:06:20 +02:00
|
|
|
public XmlStringBuilder halfOpenElement(NamedElement namedElement) {
|
|
|
|
return halfOpenElement(namedElement.getElementName());
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
public XmlStringBuilder openElement(String name) {
|
2014-08-20 21:25:14 +02:00
|
|
|
halfOpenElement(name).rightAngleBracket();
|
2014-03-20 14:35:38 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmlStringBuilder closeElement(String name) {
|
2014-03-21 09:51:52 +01:00
|
|
|
sb.append("</").append(name);
|
2014-08-20 21:25:14 +02:00
|
|
|
rightAngleBracket();
|
2014-03-20 14:35:38 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-09-11 09:49:16 +02:00
|
|
|
public XmlStringBuilder closeElement(NamedElement e) {
|
2014-07-05 11:58:13 +02:00
|
|
|
closeElement(e.getElementName());
|
2014-03-21 09:51:52 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmlStringBuilder closeEmptyElement() {
|
2014-03-20 14:35:38 +01:00
|
|
|
sb.append("/>");
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-08-20 21:25:14 +02:00
|
|
|
/**
|
2015-04-03 19:15:35 +02:00
|
|
|
* Add a right angle bracket '>'.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-08-20 21:25:14 +02:00
|
|
|
* @return a reference to this object.
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder rightAngleBracket() {
|
|
|
|
sb.append(RIGHT_ANGLE_BRACKET);
|
2014-03-21 09:51:52 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
/**
|
|
|
|
* Does nothing if value is null.
|
|
|
|
*
|
2019-08-30 12:08:30 +02:00
|
|
|
* @param name TODO javadoc me please
|
|
|
|
* @param value TODO javadoc me please
|
2014-03-21 09:51:52 +01:00
|
|
|
* @return the XmlStringBuilder
|
2014-03-20 14:35:38 +01:00
|
|
|
*/
|
|
|
|
public XmlStringBuilder attribute(String name, String value) {
|
2014-03-21 09:51:52 +01:00
|
|
|
assert value != null;
|
2014-03-20 14:35:38 +01:00
|
|
|
sb.append(' ').append(name).append("='");
|
2016-01-13 15:04:01 +01:00
|
|
|
escapeAttributeValue(value);
|
2014-03-20 14:35:38 +01:00
|
|
|
sb.append('\'');
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2016-07-20 20:57:04 +02:00
|
|
|
public XmlStringBuilder attribute(String name, boolean bool) {
|
|
|
|
return attribute(name, Boolean.toString(bool));
|
|
|
|
}
|
|
|
|
|
2015-03-30 18:24:56 +02:00
|
|
|
/**
|
|
|
|
* Add a new attribute to this builder, with the {@link java.util.Date} instance as its value,
|
2017-12-13 23:10:11 +01:00
|
|
|
* which will get formatted with {@link XmppDateTime#formatXEP0082Date(Date)}.
|
2015-03-30 18:24:56 +02:00
|
|
|
*
|
|
|
|
* @param name name of attribute
|
|
|
|
* @param value value of attribute
|
|
|
|
* @return this XmlStringBuilder
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder attribute(String name, Date value) {
|
|
|
|
assert value != null;
|
|
|
|
return attribute(name, XmppDateTime.formatXEP0082Date(value));
|
|
|
|
}
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
public XmlStringBuilder attribute(String name, CharSequence value) {
|
|
|
|
return attribute(name, value.toString());
|
|
|
|
}
|
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
public XmlStringBuilder attribute(String name, Enum<?> value) {
|
|
|
|
assert value != null;
|
2019-06-10 17:36:58 +02:00
|
|
|
// TODO: Should use toString() instead of name().
|
2014-03-21 09:51:52 +01:00
|
|
|
attribute(name, value.name());
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-06-10 16:58:38 +02:00
|
|
|
public <E extends Enum<?>> XmlStringBuilder attribute(String name, E value, E implicitDefault) {
|
|
|
|
if (value == null || value == implicitDefault) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
attribute(name, value.toString());
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-09-05 00:18:48 +02:00
|
|
|
public XmlStringBuilder attribute(String name, int value) {
|
|
|
|
assert name != null;
|
|
|
|
return attribute(name, String.valueOf(value));
|
|
|
|
}
|
|
|
|
|
2018-04-30 13:54:52 +02:00
|
|
|
public XmlStringBuilder attribute(String name, long value) {
|
|
|
|
assert name != null;
|
|
|
|
return attribute(name, String.valueOf(value));
|
|
|
|
}
|
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
public XmlStringBuilder optAttribute(String name, String value) {
|
|
|
|
if (value != null) {
|
|
|
|
attribute(name, value);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2018-04-30 13:54:52 +02:00
|
|
|
public XmlStringBuilder optAttribute(String name, Long value) {
|
|
|
|
if (value != null) {
|
|
|
|
attribute(name, value);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-03-30 18:24:56 +02:00
|
|
|
/**
|
|
|
|
* Add a new attribute to this builder, with the {@link java.util.Date} instance as its value,
|
2017-12-13 23:10:11 +01:00
|
|
|
* which will get formatted with {@link XmppDateTime#formatXEP0082Date(Date)}
|
2015-03-30 18:24:56 +02:00
|
|
|
* if {@link java.util.Date} instance is not <code>null</code>.
|
|
|
|
*
|
|
|
|
* @param name attribute name
|
|
|
|
* @param value value of this attribute
|
|
|
|
* @return this XmlStringBuilder
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder optAttribute(String name, Date value) {
|
|
|
|
if (value != null) {
|
|
|
|
attribute(name, value);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
public XmlStringBuilder optAttribute(String name, CharSequence value) {
|
|
|
|
if (value != null) {
|
|
|
|
attribute(name, value.toString());
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
public XmlStringBuilder optAttribute(String name, Enum<?> value) {
|
|
|
|
if (value != null) {
|
2014-12-27 20:47:57 +01:00
|
|
|
attribute(name, value.toString());
|
2014-03-21 09:51:52 +01:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-05-07 10:18:55 +02:00
|
|
|
public XmlStringBuilder optAttribute(String name, Number number) {
|
|
|
|
if (number != null) {
|
|
|
|
attribute(name, number.toString());
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-12-03 08:53:19 +01:00
|
|
|
/**
|
|
|
|
* Same as {@link #optAttribute(String, CharSequence)}, but with a different method name. This method can be used if
|
|
|
|
* the provided attribute value argument type causes ambiguity in method overloading. For example if the type is a
|
|
|
|
* subclass of Number and CharSequence.
|
|
|
|
*
|
|
|
|
* @param name the name of the attribute.
|
|
|
|
* @param value the value of the attribute.
|
|
|
|
* @return a reference to this object.
|
|
|
|
* @since 4.5
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder optAttributeCs(String name, CharSequence value) {
|
|
|
|
return optAttribute(name, value);
|
|
|
|
}
|
|
|
|
|
2014-07-14 19:22:02 +02:00
|
|
|
/**
|
2015-03-29 12:15:32 +02:00
|
|
|
* Add the given attribute if {@code value => 0}.
|
2014-07-14 19:22:02 +02:00
|
|
|
*
|
2019-08-30 12:08:30 +02:00
|
|
|
* @param name TODO javadoc me please
|
|
|
|
* @param value TODO javadoc me please
|
2014-07-14 19:22:02 +02:00
|
|
|
* @return a reference to this object
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder optIntAttribute(String name, int value) {
|
|
|
|
if (value >= 0) {
|
|
|
|
attribute(name, Integer.toString(value));
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-01-06 13:45:13 +01:00
|
|
|
/**
|
|
|
|
* If the provided Integer argument is not null, then add a new XML attribute with the given name and the Integer as
|
|
|
|
* value.
|
|
|
|
*
|
|
|
|
* @param name the XML attribute name.
|
|
|
|
* @param value the optional integer to use as the attribute's value.
|
|
|
|
* @return a reference to this object.
|
|
|
|
* @since 4.4.1
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder optIntAttribute(String name, Integer value) {
|
|
|
|
if (value != null) {
|
|
|
|
attribute(name, value.toString());
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-07-16 11:46:30 +02:00
|
|
|
/**
|
2015-04-03 19:15:35 +02:00
|
|
|
* Add the given attribute if value not null and {@code value => 0}.
|
2014-07-16 11:46:30 +02:00
|
|
|
*
|
2019-08-30 12:08:30 +02:00
|
|
|
* @param name TODO javadoc me please
|
|
|
|
* @param value TODO javadoc me please
|
2014-07-16 11:46:30 +02:00
|
|
|
* @return a reference to this object
|
|
|
|
*/
|
|
|
|
public XmlStringBuilder optLongAttribute(String name, Long value) {
|
2014-11-22 23:16:45 +01:00
|
|
|
if (value != null && value >= 0) {
|
2014-07-16 11:46:30 +02:00
|
|
|
attribute(name, Long.toString(value));
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-10-11 17:06:20 +02:00
|
|
|
public XmlStringBuilder optBooleanAttribute(String name, boolean bool) {
|
|
|
|
if (bool) {
|
|
|
|
sb.append(' ').append(name).append("='true'");
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-07-01 13:39:04 +02:00
|
|
|
public XmlStringBuilder optBooleanAttributeDefaultTrue(String name, boolean bool) {
|
|
|
|
if (!bool) {
|
|
|
|
sb.append(' ').append(name).append("='false'");
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2018-04-25 14:20:18 +02:00
|
|
|
private static final class XmlNsAttribute implements CharSequence {
|
|
|
|
private final String value;
|
|
|
|
private final String xmlFragment;
|
|
|
|
|
|
|
|
private XmlNsAttribute(String value) {
|
2018-07-17 15:10:39 +02:00
|
|
|
this.value = StringUtils.requireNotNullNorEmpty(value, "Value must not be null");
|
2018-04-25 14:20:18 +02:00
|
|
|
this.xmlFragment = " xmlns='" + value + '\'';
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return xmlFragment;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int length() {
|
|
|
|
return xmlFragment.length();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public char charAt(int index) {
|
|
|
|
return xmlFragment.charAt(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public CharSequence subSequence(int start, int end) {
|
|
|
|
return xmlFragment.subSequence(start, end);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
public XmlStringBuilder xmlnsAttribute(String value) {
|
2019-02-04 13:27:41 +01:00
|
|
|
if (value == null || (effectiveXmlEnvironment != null
|
|
|
|
&& effectiveXmlEnvironment.effectiveNamespaceEquals(value))) {
|
|
|
|
return this;
|
2018-04-25 14:20:18 +02:00
|
|
|
}
|
2019-02-04 13:27:41 +01:00
|
|
|
XmlNsAttribute xmlNsAttribute = new XmlNsAttribute(value);
|
|
|
|
append(xmlNsAttribute);
|
2014-03-20 14:35:38 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
public XmlStringBuilder xmllangAttribute(String value) {
|
2019-09-07 18:17:08 +02:00
|
|
|
// TODO: This should probably be attribute(), not optAttribute().
|
2014-03-21 09:51:52 +01:00
|
|
|
optAttribute("xml:lang", value);
|
|
|
|
return this;
|
|
|
|
}
|
2015-03-17 11:33:02 +01:00
|
|
|
|
2017-06-17 11:41:35 +02:00
|
|
|
public XmlStringBuilder optXmlLangAttribute(String lang) {
|
2017-08-02 01:54:29 +02:00
|
|
|
if (!StringUtils.isNullOrEmpty(lang)) {
|
2017-06-17 11:41:35 +02:00
|
|
|
xmllangAttribute(lang);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-09-17 12:31:35 +02:00
|
|
|
public XmlStringBuilder text(CharSequence text) {
|
|
|
|
assert text != null;
|
|
|
|
CharSequence escapedText = StringUtils.escapeForXmlText(text);
|
|
|
|
sb.append(escapedText);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
public XmlStringBuilder escape(String text) {
|
2014-03-21 09:51:52 +01:00
|
|
|
assert text != null;
|
2016-01-13 15:04:01 +01:00
|
|
|
sb.append(StringUtils.escapeForXml(text));
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmlStringBuilder escapeAttributeValue(String value) {
|
|
|
|
assert value != null;
|
|
|
|
sb.append(StringUtils.escapeForXmlAttributeApos(value));
|
2014-03-20 14:35:38 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-10-13 22:31:11 +02:00
|
|
|
public XmlStringBuilder optEscape(CharSequence text) {
|
|
|
|
if (text == null) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
return escape(text);
|
|
|
|
}
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
public XmlStringBuilder escape(CharSequence text) {
|
|
|
|
return escape(text.toString());
|
|
|
|
}
|
|
|
|
|
2019-09-04 09:45:53 +02:00
|
|
|
protected XmlStringBuilder prelude(FullyQualifiedElement pe) {
|
2014-11-07 21:12:01 +01:00
|
|
|
return prelude(pe.getElementName(), pe.getNamespace());
|
|
|
|
}
|
|
|
|
|
2019-09-04 09:45:53 +02:00
|
|
|
protected XmlStringBuilder prelude(String elementName, String namespace) {
|
2014-11-07 21:12:01 +01:00
|
|
|
halfOpenElement(elementName);
|
|
|
|
xmlnsAttribute(namespace);
|
2014-03-21 09:51:52 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-09-24 23:32:08 +02:00
|
|
|
public XmlStringBuilder optAppend(Element element) {
|
|
|
|
if (element != null) {
|
|
|
|
append(element.toXML(effectiveXmlEnvironment));
|
2014-03-21 09:51:52 +01:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2020-05-13 20:14:41 +02:00
|
|
|
public XmlStringBuilder optAppend(Collection<? extends Element> elements) {
|
|
|
|
if (elements != null) {
|
|
|
|
append(elements);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-09-24 23:32:08 +02:00
|
|
|
public XmlStringBuilder optTextChild(CharSequence sqc, NamedElement parentElement) {
|
|
|
|
if (sqc == null) {
|
|
|
|
return closeEmptyElement();
|
2014-11-07 21:12:01 +01:00
|
|
|
}
|
2019-09-24 23:32:08 +02:00
|
|
|
rightAngleBracket();
|
|
|
|
escape(sqc);
|
|
|
|
closeElement(parentElement);
|
2014-11-07 21:12:01 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-21 09:51:52 +01:00
|
|
|
public XmlStringBuilder append(XmlStringBuilder xsb) {
|
|
|
|
assert xsb != null;
|
|
|
|
sb.append(xsb.sb);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2019-09-07 18:17:08 +02:00
|
|
|
public XmlStringBuilder append(Element element) {
|
|
|
|
return append(element.toXML(effectiveXmlEnvironment));
|
2018-05-09 16:53:08 +02:00
|
|
|
}
|
|
|
|
|
2019-09-07 18:17:08 +02:00
|
|
|
public XmlStringBuilder append(Collection<? extends Element> elements) {
|
2015-01-01 23:57:44 +01:00
|
|
|
for (Element element : elements) {
|
2019-09-07 18:17:08 +02:00
|
|
|
append(element);
|
2015-01-01 23:57:44 +01:00
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-09-12 11:03:12 +02:00
|
|
|
public XmlStringBuilder emptyElement(Enum<?> element) {
|
2018-10-19 09:16:42 +02:00
|
|
|
// Use Enum.toString() instead Enum.name() here, since some enums override toString() in order to replace
|
|
|
|
// underscores ('_') with dash ('-') for example (name() is declared final in Enum).
|
|
|
|
return emptyElement(element.toString());
|
2014-09-12 11:03:12 +02:00
|
|
|
}
|
|
|
|
|
2014-06-19 11:48:01 +02:00
|
|
|
public XmlStringBuilder emptyElement(String element) {
|
|
|
|
halfOpenElement(element);
|
|
|
|
return closeEmptyElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmlStringBuilder condEmptyElement(boolean condition, String element) {
|
|
|
|
if (condition) {
|
|
|
|
emptyElement(element);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public XmlStringBuilder condAttribute(boolean condition, String name, String value) {
|
|
|
|
if (condition) {
|
|
|
|
attribute(name, value);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2014-03-20 14:35:38 +01:00
|
|
|
@Override
|
|
|
|
public XmlStringBuilder append(CharSequence csq) {
|
2014-03-21 09:51:52 +01:00
|
|
|
assert csq != null;
|
2014-03-20 14:35:38 +01:00
|
|
|
sb.append(csq);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public XmlStringBuilder append(CharSequence csq, int start, int end) {
|
2014-03-21 09:51:52 +01:00
|
|
|
assert csq != null;
|
2014-03-20 14:35:38 +01:00
|
|
|
sb.append(csq, start, end);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public XmlStringBuilder append(char c) {
|
|
|
|
sb.append(c);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int length() {
|
|
|
|
return sb.length();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public char charAt(int index) {
|
|
|
|
return sb.charAt(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public CharSequence subSequence(int start, int end) {
|
|
|
|
return sb.subSequence(start, end);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return sb.toString();
|
|
|
|
}
|
2014-06-19 11:48:01 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean equals(Object other) {
|
2014-08-25 13:43:44 +02:00
|
|
|
if (!(other instanceof CharSequence)) {
|
2014-06-19 11:48:01 +02:00
|
|
|
return false;
|
|
|
|
}
|
2014-08-25 13:43:44 +02:00
|
|
|
CharSequence otherCharSequenceBuilder = (CharSequence) other;
|
|
|
|
return toString().equals(otherCharSequenceBuilder.toString());
|
2014-06-19 11:48:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int hashCode() {
|
|
|
|
return toString().hashCode();
|
|
|
|
}
|
2015-05-21 20:10:31 +02:00
|
|
|
|
2019-09-21 22:56:20 +02:00
|
|
|
private static final class WrappedIoException extends RuntimeException {
|
|
|
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
|
|
|
private final IOException wrappedIoException;
|
|
|
|
|
|
|
|
private WrappedIoException(IOException wrappedIoException) {
|
|
|
|
this.wrappedIoException = wrappedIoException;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 20:10:31 +02:00
|
|
|
/**
|
|
|
|
* Write the contents of this <code>XmlStringBuilder</code> to a {@link Writer}. This will write
|
|
|
|
* the single parts one-by-one, avoiding allocation of a big continuous memory block holding the
|
|
|
|
* XmlStringBuilder contents.
|
|
|
|
*
|
2019-08-30 12:08:30 +02:00
|
|
|
* @param writer TODO javadoc me please
|
2019-09-21 23:00:17 +02:00
|
|
|
* @param enclosingXmlEnvironment the enclosing XML environment.
|
2019-10-30 12:02:36 +01:00
|
|
|
* @throws IOException if an I/O error occurred.
|
2015-05-21 20:10:31 +02:00
|
|
|
*/
|
2019-09-21 23:00:17 +02:00
|
|
|
public void write(Writer writer, XmlEnvironment enclosingXmlEnvironment) throws IOException {
|
2019-09-21 22:56:20 +02:00
|
|
|
try {
|
|
|
|
appendXmlTo(csq -> {
|
|
|
|
try {
|
|
|
|
writer.append(csq);
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new WrappedIoException(e);
|
|
|
|
}
|
|
|
|
}, enclosingXmlEnvironment);
|
|
|
|
} catch (WrappedIoException e) {
|
|
|
|
throw e.wrappedIoException;
|
|
|
|
}
|
2015-05-21 20:10:31 +02:00
|
|
|
}
|
2018-04-25 14:20:18 +02:00
|
|
|
|
2019-09-21 22:56:20 +02:00
|
|
|
public List<CharSequence> toList(XmlEnvironment enclosingXmlEnvironment) {
|
|
|
|
List<CharSequence> res = new ArrayList<>(sb.getAsList().size());
|
|
|
|
|
|
|
|
appendXmlTo(csq -> res.add(csq), enclosingXmlEnvironment);
|
|
|
|
|
|
|
|
return res;
|
2019-02-04 08:59:39 +01:00
|
|
|
}
|
|
|
|
|
2018-04-25 14:20:18 +02:00
|
|
|
@Override
|
2019-09-18 09:00:06 +02:00
|
|
|
public StringBuilder toXML(XmlEnvironment enclosingXmlEnvironment) {
|
2019-09-18 08:55:51 +02:00
|
|
|
// This is only the potential length, since the actual length depends on the given XmlEnvironment.
|
|
|
|
int potentialLength = length();
|
|
|
|
StringBuilder res = new StringBuilder(potentialLength);
|
2019-09-21 22:56:20 +02:00
|
|
|
|
|
|
|
appendXmlTo(csq -> res.append(csq), enclosingXmlEnvironment);
|
|
|
|
|
2018-04-25 14:20:18 +02:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-09-21 22:56:20 +02:00
|
|
|
private void appendXmlTo(Consumer<CharSequence> charSequenceSink, XmlEnvironment enclosingXmlEnvironment) {
|
2018-04-25 14:20:18 +02:00
|
|
|
for (CharSequence csq : sb.getAsList()) {
|
|
|
|
if (csq instanceof XmlStringBuilder) {
|
2019-09-21 22:56:20 +02:00
|
|
|
((XmlStringBuilder) csq).appendXmlTo(charSequenceSink, enclosingXmlEnvironment);
|
2018-04-25 14:20:18 +02:00
|
|
|
}
|
2018-05-09 23:06:12 +02:00
|
|
|
else if (csq instanceof XmlNsAttribute) {
|
2018-04-25 14:20:18 +02:00
|
|
|
XmlNsAttribute xmlNsAttribute = (XmlNsAttribute) csq;
|
2019-02-04 13:27:41 +01:00
|
|
|
if (!xmlNsAttribute.value.equals(enclosingXmlEnvironment.getEffectiveNamespace())) {
|
2019-09-21 22:56:20 +02:00
|
|
|
charSequenceSink.accept(xmlNsAttribute);
|
2019-02-04 13:27:41 +01:00
|
|
|
enclosingXmlEnvironment = new XmlEnvironment(xmlNsAttribute.value);
|
2018-04-25 14:20:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2019-09-21 22:56:20 +02:00
|
|
|
charSequenceSink.accept(csq);
|
2018-04-25 14:20:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-20 14:35:38 +01:00
|
|
|
}
|