StandardExtensionElement.java

  1. /**
  2.  *
  3.  * Copyright 2015-2021 Florian Schmaus.
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smack.packet;

  18. import java.util.Collections;
  19. import java.util.LinkedHashMap;
  20. import java.util.List;
  21. import java.util.Map;

  22. import javax.xml.namespace.QName;

  23. import org.jivesoftware.smack.util.MultiMap;
  24. import org.jivesoftware.smack.util.Objects;
  25. import org.jivesoftware.smack.util.StringUtils;
  26. import org.jivesoftware.smack.util.XmlStringBuilder;

  27. /**
  28.  * An {@link ExtensionElement} modeling the often required and used XML features when using XMPP. It
  29.  * is therefore suitable for most use cases. Use
  30.  * {@link StandardExtensionElement#builder(String, String)} to build these elements.
  31.  * <p>
  32.  * Note the this is only meant as catch-all if no particular extension element provider is
  33.  * registered. Protocol implementations should prefer to model their own extension elements tailored
  34.  * to their use cases.
  35.  * </p>
  36.  *
  37.  * @since 4.2
  38.  * @author Florian Schmaus
  39.  */
  40. public final class StandardExtensionElement implements XmlElement {

  41.     private final String name;
  42.     private final String namespace;
  43.     private final Map<String, String> attributes;
  44.     private final String text;
  45.     private final MultiMap<QName, StandardExtensionElement> elements;

  46.     private XmlStringBuilder xmlCache;

  47.     /**
  48.      * Constructs a new extension element with the given name and namespace and nothing else.
  49.      * <p>
  50.      * This is meant to construct extension elements used as simple flags in Stanzas.
  51.      * <p>
  52.      *
  53.      * @param name the name of the extension element.
  54.      * @param namespace the namespace of the extension element.
  55.      */
  56.     public StandardExtensionElement(String name, String namespace) {
  57.         this(name, namespace, null, null, null);
  58.     }

  59.     private StandardExtensionElement(String name, String namespace, Map<String, String> attributes, String text,
  60.                     MultiMap<QName, StandardExtensionElement> elements) {
  61.         this.name = StringUtils.requireNotNullNorEmpty(name, "Name must not be null nor empty");
  62.         this.namespace = StringUtils.requireNotNullNorEmpty(namespace, "Namespace must not be null nor empty");
  63.         if (attributes == null) {
  64.             this.attributes = Collections.emptyMap();
  65.         }
  66.         else {
  67.             this.attributes = attributes;
  68.         }
  69.         this.text = text;
  70.         this.elements = elements;
  71.     }

  72.     @Override
  73.     public String getElementName() {
  74.         return name;
  75.     }

  76.     @Override
  77.     public String getNamespace() {
  78.         return namespace;
  79.     }

  80.     public String getAttributeValue(String attribute) {
  81.         return attributes.get(attribute);
  82.     }

  83.     public Map<String, String> getAttributes() {
  84.         return Collections.unmodifiableMap(attributes);
  85.     }

  86.     public StandardExtensionElement getFirstElement(String element, String namespace) {
  87.         if (elements == null) {
  88.             return null;
  89.         }
  90.         QName key = new QName(namespace, element);
  91.         return elements.getFirst(key);
  92.     }

  93.     public StandardExtensionElement getFirstElement(String element) {
  94.         return getFirstElement(element, namespace);
  95.     }

  96.     public List<StandardExtensionElement> getElements(String element, String namespace) {
  97.         if (elements == null) {
  98.             return null;
  99.         }
  100.         QName key = new QName(namespace, element);
  101.         return elements.getAll(key);
  102.     }

  103.     public List<StandardExtensionElement> getElements(String element) {
  104.         return getElements(element, namespace);
  105.     }

  106.     public List<StandardExtensionElement> getElements() {
  107.         if (elements == null) {
  108.             return Collections.emptyList();
  109.         }
  110.         return elements.values();
  111.     }

  112.     public String getText() {
  113.         return text;
  114.     }

  115.     @Override
  116.     public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
  117.         if (xmlCache != null) {
  118.             return xmlCache;
  119.         }
  120.         XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
  121.         for (Map.Entry<String, String> entry : attributes.entrySet()) {
  122.             xml.attribute(entry.getKey(), entry.getValue());
  123.         }
  124.         xml.rightAngleBracket();

  125.         if (text != null) {
  126.             xml.text(text);
  127.         }

  128.         if (elements != null) {
  129.             for (Map.Entry<QName, StandardExtensionElement> entry : elements.entrySet()) {
  130.                 xml.append(entry.getValue().toXML(getNamespace()));
  131.             }
  132.         }

  133.         xml.closeElement(this);
  134.         xmlCache = xml;
  135.         return xml;
  136.     }

  137.     public static Builder builder(String name, String namespace) {
  138.         return new Builder(name, namespace);
  139.     }

  140.     public static final class Builder {
  141.         private final String name;
  142.         private final String namespace;

  143.         private Map<String, String> attributes;
  144.         private String text;
  145.         private MultiMap<QName, StandardExtensionElement> elements;

  146.         private Builder(String name, String namespace) {
  147.             this.name = name;
  148.             this.namespace = namespace;
  149.         }

  150.         public Builder addAttribute(String name, String value) {
  151.             StringUtils.requireNotNullNorEmpty(name, "Attribute name must be set");
  152.             Objects.requireNonNull(value, "Attribute value must be not null");
  153.             if (attributes == null) {
  154.                 attributes = new LinkedHashMap<>();
  155.             }
  156.             attributes.put(name, value);
  157.             return this;
  158.         }

  159.         public Builder addAttributes(Map<String, String> attributes) {
  160.             if (this.attributes == null) {
  161.                 this.attributes = new LinkedHashMap<>(attributes.size());
  162.             }
  163.             this.attributes.putAll(attributes);
  164.             return this;
  165.         }

  166.         public Builder setText(String text) {
  167.             this.text = Objects.requireNonNull(text, "Text must be not null");
  168.             return this;
  169.         }

  170.         public Builder addElement(StandardExtensionElement element) {
  171.             Objects.requireNonNull(element, "Element must not be null");
  172.             if (elements == null) {
  173.                 elements = new MultiMap<>();
  174.             }

  175.             QName key = element.getQName();
  176.             elements.put(key, element);
  177.             return this;
  178.         }

  179.         public Builder addElement(String name, String textValue) {
  180.             StandardExtensionElement element = StandardExtensionElement.builder(name, this.namespace).setText(
  181.                             textValue).build();
  182.             return addElement(element);
  183.         }

  184.         public StandardExtensionElement build() {
  185.             return new StandardExtensionElement(name, namespace, attributes, text, elements);
  186.         }
  187.     }
  188. }