/** * * Copyright 2015-2021 Florian Schmaus. * * 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.packet; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import org.jivesoftware.smack.util.MultiMap; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; /** * An {@link ExtensionElement} modeling the often required and used XML features when using XMPP. It * is therefore suitable for most use cases. Use * {@link StandardExtensionElement#builder(String, String)} to build these elements. *

* Note the this is only meant as catch-all if no particular extension element provider is * registered. Protocol implementations should prefer to model their own extension elements tailored * to their use cases. *

* * @since 4.2 * @author Florian Schmaus */ public final class StandardExtensionElement implements XmlElement { private final String name; private final String namespace; private final Map attributes; private final String text; private final MultiMap elements; private XmlStringBuilder xmlCache; /** * Constructs a new extension element with the given name and namespace and nothing else. *

* This is meant to construct extension elements used as simple flags in Stanzas. *

* * @param name the name of the extension element. * @param namespace the namespace of the extension element. */ public StandardExtensionElement(String name, String namespace) { this(name, namespace, null, null, null); } private StandardExtensionElement(String name, String namespace, Map attributes, String text, MultiMap elements) { this.name = StringUtils.requireNotNullNorEmpty(name, "Name must not be null nor empty"); this.namespace = StringUtils.requireNotNullNorEmpty(namespace, "Namespace must not be null nor empty"); if (attributes == null) { this.attributes = Collections.emptyMap(); } else { this.attributes = attributes; } this.text = text; this.elements = elements; } @Override public String getElementName() { return name; } @Override public String getNamespace() { return namespace; } public String getAttributeValue(String attribute) { return attributes.get(attribute); } public Map getAttributes() { return Collections.unmodifiableMap(attributes); } public StandardExtensionElement getFirstElement(String element, String namespace) { if (elements == null) { return null; } QName key = new QName(namespace, element); return elements.getFirst(key); } public StandardExtensionElement getFirstElement(String element) { return getFirstElement(element, namespace); } public List getElements(String element, String namespace) { if (elements == null) { return null; } QName key = new QName(namespace, element); return elements.getAll(key); } public List getElements(String element) { return getElements(element, namespace); } public List getElements() { if (elements == null) { return Collections.emptyList(); } return elements.values(); } public String getText() { return text; } @Override public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { if (xmlCache != null) { return xmlCache; } XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); for (Map.Entry entry : attributes.entrySet()) { xml.attribute(entry.getKey(), entry.getValue()); } xml.rightAngleBracket(); if (text != null) { xml.text(text); } if (elements != null) { for (Map.Entry entry : elements.entrySet()) { xml.append(entry.getValue().toXML(getNamespace())); } } xml.closeElement(this); xmlCache = xml; return xml; } public static Builder builder(String name, String namespace) { return new Builder(name, namespace); } public static final class Builder { private final String name; private final String namespace; private Map attributes; private String text; private MultiMap elements; private Builder(String name, String namespace) { this.name = name; this.namespace = namespace; } public Builder addAttribute(String name, String value) { StringUtils.requireNotNullNorEmpty(name, "Attribute name must be set"); Objects.requireNonNull(value, "Attribute value must be not null"); if (attributes == null) { attributes = new LinkedHashMap<>(); } attributes.put(name, value); return this; } public Builder addAttributes(Map attributes) { if (this.attributes == null) { this.attributes = new LinkedHashMap<>(attributes.size()); } this.attributes.putAll(attributes); return this; } public Builder setText(String text) { this.text = Objects.requireNonNull(text, "Text must be not null"); return this; } public Builder addElement(StandardExtensionElement element) { Objects.requireNonNull(element, "Element must not be null"); if (elements == null) { elements = new MultiMap<>(); } QName key = element.getQName(); elements.put(key, element); return this; } public Builder addElement(String name, String textValue) { StandardExtensionElement element = StandardExtensionElement.builder(name, this.namespace).setText( textValue).build(); return addElement(element); } public StandardExtensionElement build() { return new StandardExtensionElement(name, namespace, attributes, text, elements); } } }