001/** 002 * 003 * Copyright 2015 Florian Schmaus. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.packet; 018 019import java.util.Collections; 020import java.util.LinkedHashMap; 021import java.util.List; 022import java.util.Map; 023 024import org.jivesoftware.smack.util.MultiMap; 025import org.jivesoftware.smack.util.Objects; 026import org.jivesoftware.smack.util.StringUtils; 027import org.jivesoftware.smack.util.XmlStringBuilder; 028import org.jxmpp.util.XmppStringUtils; 029 030/** 031 * An {@link ExtensionElement} modeling the often required and used XML features when using XMPP. It 032 * is therefore suitable for most use cases. Use 033 * {@link StandardExtensionElement#builder(String, String)} to build these elements. 034 * <p> 035 * Note the this is only meant as catch-all if no particular extension element provider is 036 * registered. Protocol implementations should prefer to model their own extension elements tailored 037 * to their use cases. 038 * </p> 039 * 040 * @since 4.2 041 * @author Florian Schmaus 042 */ 043public final class StandardExtensionElement implements ExtensionElement { 044 045 private final String name; 046 private final String namespace; 047 private final Map<String, String> attributes; 048 private final String text; 049 private final MultiMap<String, StandardExtensionElement> elements; 050 051 private XmlStringBuilder xmlCache; 052 053 /** 054 * Constructs a new extension element with the given name and namespace and nothing else. 055 * <p> 056 * This is meant to construct extension elements used as simple flags in Stanzas. 057 * <p> 058 * 059 * @param name the name of the extension element. 060 * @param namespace the namespace of the extension element. 061 */ 062 public StandardExtensionElement(String name, String namespace) { 063 this(name, namespace, null, null, null); 064 } 065 066 private StandardExtensionElement(String name, String namespace, Map<String, String> attributes, String text, 067 MultiMap<String, StandardExtensionElement> elements) { 068 this.name = StringUtils.requireNotNullOrEmpty(name, "Name must not be null or empty"); 069 this.namespace = StringUtils.requireNotNullOrEmpty(namespace, "Namespace must not be null or empty"); 070 if (attributes == null) { 071 this.attributes = Collections.emptyMap(); 072 } 073 else { 074 this.attributes = attributes; 075 } 076 this.text = text; 077 this.elements = elements; 078 } 079 080 @Override 081 public String getElementName() { 082 return name; 083 } 084 085 @Override 086 public String getNamespace() { 087 return namespace; 088 } 089 090 public String getAttributeValue(String attribute) { 091 return attributes.get(attribute); 092 } 093 094 public Map<String, String> getAttributes() { 095 return Collections.unmodifiableMap(attributes); 096 } 097 098 public StandardExtensionElement getFirstElement(String element, String namespace) { 099 if (elements == null) { 100 return null; 101 } 102 String key = XmppStringUtils.generateKey(element, namespace); 103 return elements.getFirst(key); 104 } 105 106 public StandardExtensionElement getFirstElement(String element) { 107 return getFirstElement(element, namespace); 108 } 109 110 public List<StandardExtensionElement> getElements(String element, String namespace) { 111 if (elements == null) { 112 return null; 113 } 114 String key = XmppStringUtils.generateKey(element, namespace); 115 return elements.getAll(key); 116 } 117 118 public List<StandardExtensionElement> getElements(String element) { 119 return getElements(element, namespace); 120 } 121 122 public List<StandardExtensionElement> getElements() { 123 if (elements == null){ 124 return Collections.emptyList(); 125 } 126 return elements.values(); 127 } 128 129 public String getText() { 130 return text; 131 } 132 133 @Override 134 public XmlStringBuilder toXML() { 135 return toXML(null); 136 } 137 138 public XmlStringBuilder toXML(String enclosingNamespace) { 139 if (xmlCache != null) { 140 return xmlCache; 141 } 142 XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace); 143 for (Map.Entry<String, String> entry : attributes.entrySet()) { 144 xml.attribute(entry.getKey(), entry.getValue()); 145 } 146 xml.rightAngleBracket(); 147 148 xml.optEscape(text); 149 150 if (elements != null) { 151 for (Map.Entry<String, StandardExtensionElement> entry : elements.entrySet()) { 152 xml.append(entry.getValue().toXML(getNamespace())); 153 } 154 } 155 156 xml.closeElement(this); 157 xmlCache = xml; 158 return xml; 159 } 160 161 public static Builder builder(String name, String namespace) { 162 return new Builder(name, namespace); 163 } 164 165 public static final class Builder { 166 private final String name; 167 private final String namespace; 168 169 private Map<String, String> attributes; 170 private String text; 171 private MultiMap<String, StandardExtensionElement> elements; 172 173 private Builder(String name, String namespace) { 174 this.name = name; 175 this.namespace = namespace; 176 } 177 178 public Builder addAttribute(String name, String value) { 179 StringUtils.requireNotNullOrEmpty(name, "Attribute name must be set"); 180 Objects.requireNonNull(value, "Attribute value must be not null"); 181 if (attributes == null) { 182 attributes = new LinkedHashMap<>(); 183 } 184 attributes.put(name, value); 185 return this; 186 } 187 188 public Builder addAttributes(Map<String, String> attributes) { 189 if (this.attributes == null) { 190 this.attributes = new LinkedHashMap<>(attributes.size()); 191 } 192 this.attributes.putAll(attributes); 193 return this; 194 } 195 196 public Builder setText(String text) { 197 this.text = Objects.requireNonNull(text, "Text must be not null"); 198 return this; 199 } 200 201 public Builder addElement(StandardExtensionElement element) { 202 Objects.requireNonNull(element, "Element must not be null"); 203 if (elements == null) { 204 elements = new MultiMap<>(); 205 } 206 String key = XmppStringUtils.generateKey(element.getElementName(), element.getNamespace()); 207 elements.put(key, element); 208 return this; 209 } 210 211 public Builder addElement(String name, String textValue) { 212 StandardExtensionElement element = StandardExtensionElement.builder(name, this.namespace).setText( 213 textValue).build(); 214 return addElement(element); 215 } 216 217 public StandardExtensionElement build() { 218 return new StandardExtensionElement(name, namespace, attributes, text, elements); 219 } 220 } 221}