mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-26 00:02:06 +01:00
Add StandardExtensionElement
replaces DefaultExtensionElement which is now deprecated. Also changes Stanza (and MultiMap) API so that there can be duplicate extension elements, as this change is required for StandardExtensionElement and by the XMPP standard.
This commit is contained in:
parent
6ede7d0409
commit
227ef2c5ae
9 changed files with 445 additions and 73 deletions
|
@ -45,7 +45,9 @@ import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
* cases, a custom ExtensionElementProvider should be used.
|
* cases, a custom ExtensionElementProvider should be used.
|
||||||
*
|
*
|
||||||
* @author Matt Tucker
|
* @author Matt Tucker
|
||||||
|
* @deprecated use {@link org.jivesoftware.smack.packet.StandardExtensionElement} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class DefaultExtensionElement implements ExtensionElement {
|
public class DefaultExtensionElement implements ExtensionElement {
|
||||||
|
|
||||||
private String elementName;
|
private String elementName;
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2015 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 org.jivesoftware.smack.util.MultiMap;
|
||||||
|
import org.jivesoftware.smack.util.Objects;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
import org.jxmpp.util.XmppStringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* <p>
|
||||||
|
* 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.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @since 4.2
|
||||||
|
* @author Florian Schmaus
|
||||||
|
*/
|
||||||
|
public final class StandardExtensionElement implements ExtensionElement {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final String namespace;
|
||||||
|
private final Map<String, String> attributes;
|
||||||
|
private final String text;
|
||||||
|
private final MultiMap<String, StandardExtensionElement> elements;
|
||||||
|
|
||||||
|
private XmlStringBuilder xmlCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new extension element with the given name and namespace and nothing else.
|
||||||
|
* <p>
|
||||||
|
* This is meant to construct extension elements used as simple flags in Stanzas.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @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<String, String> attributes, String text,
|
||||||
|
MultiMap<String, StandardExtensionElement> elements) {
|
||||||
|
this.name = StringUtils.requireNotNullOrEmpty(name, "Name must not be null or empty");
|
||||||
|
this.namespace = StringUtils.requireNotNullOrEmpty(namespace, "Namespace must not be null or 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<String, String> getAttributes() {
|
||||||
|
return Collections.unmodifiableMap(attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardExtensionElement getFirstElement(String element, String namespace) {
|
||||||
|
if (elements == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String key = XmppStringUtils.generateKey(element, namespace);
|
||||||
|
return elements.getFirst(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StandardExtensionElement getFirstElement(String element) {
|
||||||
|
return getFirstElement(element, namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<StandardExtensionElement> getElements(String element, String namespace) {
|
||||||
|
if (elements == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String key = XmppStringUtils.generateKey(element, namespace);
|
||||||
|
return elements.getAll(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<StandardExtensionElement> getElements(String element) {
|
||||||
|
return getElements(element, namespace);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<StandardExtensionElement> getElements() {
|
||||||
|
if (elements == null){
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
return elements.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XmlStringBuilder toXML() {
|
||||||
|
return toXML(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder toXML(String enclosingNamespace) {
|
||||||
|
if (xmlCache != null) {
|
||||||
|
return xmlCache;
|
||||||
|
}
|
||||||
|
XmlStringBuilder xml = new XmlStringBuilder(this, enclosingNamespace);
|
||||||
|
for (Map.Entry<String, String> entry : attributes.entrySet()) {
|
||||||
|
xml.attribute(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
xml.rightAngleBracket();
|
||||||
|
|
||||||
|
xml.optEscape(text);
|
||||||
|
|
||||||
|
if (elements != null) {
|
||||||
|
for (Map.Entry<String, StandardExtensionElement> 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<String, String> attributes;
|
||||||
|
private String text;
|
||||||
|
private MultiMap<String, StandardExtensionElement> elements;
|
||||||
|
|
||||||
|
private Builder(String name, String namespace) {
|
||||||
|
this.name = name;
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder addAttribute(String name, String value) {
|
||||||
|
StringUtils.requireNotNullOrEmpty(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<String, String> 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<>();
|
||||||
|
}
|
||||||
|
String key = XmppStringUtils.generateKey(element.getElementName(), element.getNamespace());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,7 +31,6 @@ import org.jxmpp.util.XmppStringUtils;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for XMPP Stanzas, which are called Stanza(/Packet) in older versions of Smack (i.e. < 4.1).
|
* Base class for XMPP Stanzas, which are called Stanza(/Packet) in older versions of Smack (i.e. < 4.1).
|
||||||
|
@ -285,7 +284,7 @@ public abstract class Stanza implements TopLevelStreamElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a set of all extensions with the given element name <em>and</em> namespace.
|
* Return a list of all extensions with the given element name <em>and</em> namespace.
|
||||||
* <p>
|
* <p>
|
||||||
* Changes to the returned set will update the stanza(/packet) extensions, if the returned set is not the empty set.
|
* Changes to the returned set will update the stanza(/packet) extensions, if the returned set is not the empty set.
|
||||||
* </p>
|
* </p>
|
||||||
|
@ -295,7 +294,7 @@ public abstract class Stanza implements TopLevelStreamElement {
|
||||||
* @return a set of all matching extensions.
|
* @return a set of all matching extensions.
|
||||||
* @since 4.1
|
* @since 4.1
|
||||||
*/
|
*/
|
||||||
public Set<ExtensionElement> getExtensions(String elementName, String namespace) {
|
public List<ExtensionElement> getExtensions(String elementName, String namespace) {
|
||||||
requireNotNullOrEmpty(elementName, "elementName must not be null or empty");
|
requireNotNullOrEmpty(elementName, "elementName must not be null or empty");
|
||||||
requireNotNullOrEmpty(namespace, "namespace must not be null or empty");
|
requireNotNullOrEmpty(namespace, "namespace must not be null or empty");
|
||||||
String key = XmppStringUtils.generateKey(elementName, namespace);
|
String key = XmppStringUtils.generateKey(elementName, namespace);
|
||||||
|
@ -319,12 +318,7 @@ public abstract class Stanza implements TopLevelStreamElement {
|
||||||
* Returns the first extension that matches the specified element name and
|
* Returns the first extension that matches the specified element name and
|
||||||
* namespace, or <tt>null</tt> if it doesn't exist. If the provided elementName is null,
|
* namespace, or <tt>null</tt> if it doesn't exist. If the provided elementName is null,
|
||||||
* only the namespace is matched. Extensions are
|
* only the namespace is matched. Extensions are
|
||||||
* are arbitrary XML sub-documents in standard XMPP packets. By default, a
|
* are arbitrary XML elements in standard XMPP stanzas.
|
||||||
* {@link DefaultExtensionElement} instance will be returned for each extension. However,
|
|
||||||
* ExtensionElementProvider instances can be registered with the
|
|
||||||
* {@link org.jivesoftware.smack.provider.ProviderManager ProviderManager}
|
|
||||||
* class to handle custom parsing. In that case, the type of the Object
|
|
||||||
* will be determined by the provider.
|
|
||||||
*
|
*
|
||||||
* @param elementName the XML element name of the extension. (May be null)
|
* @param elementName the XML element name of the extension. (May be null)
|
||||||
* @param namespace the XML element namespace of the extension.
|
* @param namespace the XML element namespace of the extension.
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.parsing;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.StandardExtensionElement.Builder;
|
||||||
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
|
import org.jivesoftware.smack.util.ParserUtils;
|
||||||
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parser for {@link StandardExtensionElement}s.
|
||||||
|
*
|
||||||
|
* @author Florian Schmaus
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class StandardExtensionElementProvider extends ExtensionElementProvider<StandardExtensionElement> {
|
||||||
|
|
||||||
|
public static StandardExtensionElementProvider INSTANCE = new StandardExtensionElementProvider();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StandardExtensionElement parse(final XmlPullParser parser, final int initialDepth)
|
||||||
|
throws XmlPullParserException, IOException {
|
||||||
|
// Unlike most (all?) other providers, we don't know the name and namespace of the element
|
||||||
|
// we are parsing here.
|
||||||
|
String name = parser.getName();
|
||||||
|
String namespace = parser.getNamespace();
|
||||||
|
Builder builder = StandardExtensionElement.builder(name, namespace);
|
||||||
|
final int namespaceCount = parser.getNamespaceCount(initialDepth);
|
||||||
|
final int attributeCount = parser.getAttributeCount();
|
||||||
|
final Map<String, String> attributes = new LinkedHashMap<>(namespaceCount + attributeCount);
|
||||||
|
for (int i = 0; i < namespaceCount; i++) {
|
||||||
|
String nsprefix = parser.getNamespacePrefix(i);
|
||||||
|
if (nsprefix == null) {
|
||||||
|
// Skip the default namespace.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// XmlPullParser must either return null or a non-empty String.
|
||||||
|
assert StringUtils.isNotEmpty(nsprefix);
|
||||||
|
String nsuri = parser.getNamespaceUri(i);
|
||||||
|
attributes.put("xmlns:" + nsprefix, nsuri);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < attributeCount; i++) {
|
||||||
|
String attributePrefix = parser.getAttributePrefix(i);
|
||||||
|
String attributeName = parser.getAttributeName(i);
|
||||||
|
String attributeValue = parser.getAttributeValue(i);
|
||||||
|
String attributeKey;
|
||||||
|
if (StringUtils.isNullOrEmpty(attributePrefix)) {
|
||||||
|
attributeKey = attributeName;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
attributeKey = attributePrefix + ':' + attributeName;
|
||||||
|
}
|
||||||
|
attributes.put(attributeKey, attributeValue);
|
||||||
|
}
|
||||||
|
builder.addAttributes(attributes);
|
||||||
|
|
||||||
|
outerloop: while (true) {
|
||||||
|
int event = parser.next();
|
||||||
|
switch (event) {
|
||||||
|
case XmlPullParser.START_TAG:
|
||||||
|
builder.addElement(parse(parser, parser.getDepth()));
|
||||||
|
break;
|
||||||
|
case XmlPullParser.TEXT:
|
||||||
|
builder.setText(parser.getText());
|
||||||
|
break;
|
||||||
|
case XmlPullParser.END_TAG:
|
||||||
|
if (initialDepth == parser.getDepth()) {
|
||||||
|
break outerloop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserUtils.assertAtEndTag(parser);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ import java.util.Set;
|
||||||
/**
|
/**
|
||||||
* A lightweight implementation of a MultiMap, that is a Map that is able to hold multiple values for every key.
|
* A lightweight implementation of a MultiMap, that is a Map that is able to hold multiple values for every key.
|
||||||
* <p>
|
* <p>
|
||||||
* This MultiMap uses a LinkedHashMap together with a LinkedHashSet in order to keep the order of its entries.
|
* This MultiMap uses a {@link LinkedHashMap} together with a {@link ArrayList} in order to keep the order of its entries.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param <K> the type of the keys the map uses.
|
* @param <K> the type of the keys the map uses.
|
||||||
|
@ -36,13 +36,13 @@ import java.util.Set;
|
||||||
public class MultiMap<K,V> {
|
public class MultiMap<K,V> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The constant value '6'.
|
* The constant value {@value}.
|
||||||
*/
|
*/
|
||||||
public static final int DEFAULT_MAP_SIZE = 6;
|
public static final int DEFAULT_MAP_SIZE = 6;
|
||||||
|
|
||||||
private static final int ENTRY_SET_SIZE = 3;
|
private static final int ENTRY_LIST_SIZE = 3;
|
||||||
|
|
||||||
private final Map<K, Set<V>> map;
|
private final Map<K, List<V>> map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new MultiMap with a initial capacity of {@link #DEFAULT_MAP_SIZE}.
|
* Constructs a new MultiMap with a initial capacity of {@link #DEFAULT_MAP_SIZE}.
|
||||||
|
@ -62,8 +62,8 @@ public class MultiMap<K,V> {
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
int size = 0;
|
int size = 0;
|
||||||
for (Set<V> set : map.values()) {
|
for (List<V> list : map.values()) {
|
||||||
size += set.size();
|
size += list.size();
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,8 @@ public class MultiMap<K,V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsValue(Object value) {
|
public boolean containsValue(Object value) {
|
||||||
for (Set<V> set : map.values()) {
|
for (List<V> list : map.values()) {
|
||||||
if (set.contains(value)) {
|
if (list.contains(value)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ public class MultiMap<K,V> {
|
||||||
* @return the first value or null.
|
* @return the first value or null.
|
||||||
*/
|
*/
|
||||||
public V getFirst(Object key) {
|
public V getFirst(Object key) {
|
||||||
Set<V> res = getAll(key);
|
List<V> res = getAll(key);
|
||||||
if (res.isEmpty()) {
|
if (res.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,24 +109,24 @@ public class MultiMap<K,V> {
|
||||||
* @param key
|
* @param key
|
||||||
* @return all values for the given key.
|
* @return all values for the given key.
|
||||||
*/
|
*/
|
||||||
public Set<V> getAll(Object key) {
|
public List<V> getAll(Object key) {
|
||||||
Set<V> res = map.get(key);
|
List<V> res = map.get(key);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
res = Collections.emptySet();
|
res = Collections.emptyList();
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean put(K key, V value) {
|
public boolean put(K key, V value) {
|
||||||
boolean keyExisted;
|
boolean keyExisted;
|
||||||
Set<V> set = map.get(key);
|
List<V> list = map.get(key);
|
||||||
if (set == null) {
|
if (list == null) {
|
||||||
set = new LinkedHashSet<>(ENTRY_SET_SIZE);
|
list = new ArrayList<>(ENTRY_LIST_SIZE);
|
||||||
set.add(value);
|
list.add(value);
|
||||||
map.put(key, set);
|
map.put(key, list);
|
||||||
keyExisted = false;
|
keyExisted = false;
|
||||||
} else {
|
} else {
|
||||||
set.add(value);
|
list.add(value);
|
||||||
keyExisted = true;
|
keyExisted = true;
|
||||||
}
|
}
|
||||||
return keyExisted;
|
return keyExisted;
|
||||||
|
@ -139,7 +139,7 @@ public class MultiMap<K,V> {
|
||||||
* @return the first value of the given key or null.
|
* @return the first value of the given key or null.
|
||||||
*/
|
*/
|
||||||
public V remove(Object key) {
|
public V remove(Object key) {
|
||||||
Set<V> res = map.remove(key);
|
List<V> res = map.remove(key);
|
||||||
if (res == null) {
|
if (res == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -158,12 +158,12 @@ public class MultiMap<K,V> {
|
||||||
* @return true if the mapping was removed, false otherwise.
|
* @return true if the mapping was removed, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean removeOne(Object key, V value) {
|
public boolean removeOne(Object key, V value) {
|
||||||
Set<V> set = map.get(key);
|
List<V> list = map.get(key);
|
||||||
if (set == null) {
|
if (list == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
boolean res = set.remove(value);
|
boolean res = list.remove(value);
|
||||||
if (set.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
// Remove the key also if the value set is now empty
|
// Remove the key also if the value set is now empty
|
||||||
map.remove(key);
|
map.remove(key);
|
||||||
}
|
}
|
||||||
|
@ -191,15 +191,15 @@ public class MultiMap<K,V> {
|
||||||
*/
|
*/
|
||||||
public List<V> values() {
|
public List<V> values() {
|
||||||
List<V> values = new ArrayList<>(size());
|
List<V> values = new ArrayList<>(size());
|
||||||
for (Set<V> set : map.values()) {
|
for (List<V> list : map.values()) {
|
||||||
values.addAll(set);
|
values.addAll(list);
|
||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Map.Entry<K, V>> entrySet() {
|
public Set<Map.Entry<K, V>> entrySet() {
|
||||||
Set<Map.Entry<K, V>> entrySet = new LinkedHashSet<>(size());
|
Set<Map.Entry<K, V>> entrySet = new LinkedHashSet<>(size());
|
||||||
for (Map.Entry<K, Set<V>> entries : map.entrySet()) {
|
for (Map.Entry<K, List<V>> entries : map.entrySet()) {
|
||||||
K key = entries.getKey();
|
K key = entries.getKey();
|
||||||
for (V value : entries.getValue()) {
|
for (V value : entries.getValue()) {
|
||||||
entrySet.add(new SimpleMapEntry<>(key, value));
|
entrySet.add(new SimpleMapEntry<>(key, value));
|
||||||
|
|
|
@ -29,7 +29,6 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.compress.packet.Compress;
|
import org.jivesoftware.smack.compress.packet.Compress;
|
||||||
import org.jivesoftware.smack.packet.DefaultExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.EmptyResultIQ;
|
import org.jivesoftware.smack.packet.EmptyResultIQ;
|
||||||
import org.jivesoftware.smack.packet.ErrorIQ;
|
import org.jivesoftware.smack.packet.ErrorIQ;
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
|
@ -42,6 +41,7 @@ import org.jivesoftware.smack.packet.StartTls;
|
||||||
import org.jivesoftware.smack.packet.StreamError;
|
import org.jivesoftware.smack.packet.StreamError;
|
||||||
import org.jivesoftware.smack.packet.UnparsedIQ;
|
import org.jivesoftware.smack.packet.UnparsedIQ;
|
||||||
import org.jivesoftware.smack.packet.XMPPError;
|
import org.jivesoftware.smack.packet.XMPPError;
|
||||||
|
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.IQProvider;
|
import org.jivesoftware.smack.provider.IQProvider;
|
||||||
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
import org.jivesoftware.smack.provider.ExtensionElementProvider;
|
||||||
import org.jivesoftware.smack.provider.ProviderManager;
|
import org.jivesoftware.smack.provider.ProviderManager;
|
||||||
|
@ -918,34 +918,8 @@ public class PacketParserUtils {
|
||||||
return provider.parse(parser);
|
return provider.parse(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
final int initialDepth = parser.getDepth();
|
|
||||||
// No providers registered, so use a default extension.
|
// No providers registered, so use a default extension.
|
||||||
DefaultExtensionElement extension = new DefaultExtensionElement(elementName, namespace);
|
return StandardExtensionElementProvider.INSTANCE.parse(parser);
|
||||||
outerloop: while (true) {
|
|
||||||
int eventType = parser.next();
|
|
||||||
switch (eventType) {
|
|
||||||
case XmlPullParser.START_TAG:
|
|
||||||
String name = parser.getName();
|
|
||||||
// If an empty element, set the value with the empty string.
|
|
||||||
if (parser.isEmptyElementTag()) {
|
|
||||||
extension.setValue(name,"");
|
|
||||||
}
|
|
||||||
// Otherwise, get the the element text.
|
|
||||||
else {
|
|
||||||
eventType = parser.next();
|
|
||||||
if (eventType == XmlPullParser.TEXT) {
|
|
||||||
String value = parser.getText();
|
|
||||||
extension.setValue(name, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case XmlPullParser.END_TAG:
|
|
||||||
if (parser.getDepth() == initialDepth) {
|
|
||||||
break outerloop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return extension;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StartTls parseStartTlsFeature(XmlPullParser parser)
|
public static StartTls parseStartTlsFeature(XmlPullParser parser)
|
||||||
|
|
|
@ -45,6 +45,16 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
||||||
halfOpenElement(e.getElementName());
|
halfOpenElement(e.getElementName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder(ExtensionElement ee, String enclosingNamespace) {
|
||||||
|
this();
|
||||||
|
String namespace = ee.getNamespace();
|
||||||
|
if (namespace.equals(enclosingNamespace)) {
|
||||||
|
halfOpenElement(ee.getElementName());
|
||||||
|
} else {
|
||||||
|
prelude(ee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public XmlStringBuilder escapedElement(String name, String escapedContent) {
|
public XmlStringBuilder escapedElement(String name, String escapedContent) {
|
||||||
assert escapedContent != null;
|
assert escapedContent != null;
|
||||||
openElement(name);
|
openElement(name);
|
||||||
|
@ -351,6 +361,13 @@ public class XmlStringBuilder implements Appendable, CharSequence {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XmlStringBuilder optEscape(CharSequence text) {
|
||||||
|
if (text == null) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return escape(text);
|
||||||
|
}
|
||||||
|
|
||||||
public XmlStringBuilder escape(CharSequence text) {
|
public XmlStringBuilder escape(CharSequence text) {
|
||||||
return escape(text.toString());
|
return escape(text.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2015 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.parsing;
|
||||||
|
|
||||||
|
import static org.jivesoftware.smack.util.PacketParserUtils.getParserFor;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
|
import org.jivesoftware.smack.packet.StandardExtensionElement.Builder;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class StandardExtensionElementParserTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildAndParse() throws Exception {
|
||||||
|
Builder builder = StandardExtensionElement.builder("foo", "ns1");
|
||||||
|
builder.addAttribute("attr1", "attr1-value");
|
||||||
|
builder.addElement(StandardExtensionElement.builder("bar", "ns2").addAttribute("attr2", "attr2-value").build());
|
||||||
|
builder.addElement("another-element", "another-element-text");
|
||||||
|
final String elementString = builder.build().toXML().toString();
|
||||||
|
|
||||||
|
StandardExtensionElement parsedElement = StandardExtensionElementProvider.INSTANCE.parse(getParserFor(elementString));
|
||||||
|
|
||||||
|
assertEquals("foo", parsedElement.getElementName());
|
||||||
|
assertEquals("ns1", parsedElement.getNamespace());
|
||||||
|
assertEquals("attr1-value", parsedElement.getAttributeValue("attr1"));
|
||||||
|
|
||||||
|
StandardExtensionElement barNs2Element = parsedElement.getFirstElement("bar", "ns2");
|
||||||
|
assertEquals("bar", barNs2Element.getElementName());
|
||||||
|
assertEquals("ns2", barNs2Element.getNamespace());
|
||||||
|
assertEquals("attr2-value", barNs2Element.getAttributeValue("attr2"));
|
||||||
|
|
||||||
|
assertEquals("another-element-text", parsedElement.getFirstElement("another-element").getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWithAttrNamespacesAndParse() throws Exception {
|
||||||
|
Builder builder = StandardExtensionElement.builder("foo", "ns1-value");
|
||||||
|
builder.addAttribute("xmlns:ns2", "ns2-value");
|
||||||
|
builder.addAttribute("ns2:bar", "bar-ns2-value");
|
||||||
|
final String elementString = builder.build().toXML().toString();
|
||||||
|
|
||||||
|
StandardExtensionElement parsedElement = StandardExtensionElementProvider.INSTANCE.parse(getParserFor(elementString));
|
||||||
|
assertEquals("foo", parsedElement.getElementName());
|
||||||
|
assertEquals("ns1-value", parsedElement.getNamespace());
|
||||||
|
String barNs2Value = parsedElement.getAttributeValue("ns2:bar");
|
||||||
|
assertEquals("bar-ns2-value", barNs2Value);
|
||||||
|
String ns2Value = parsedElement.getAttributeValue("xmlns:ns2");
|
||||||
|
assertEquals("ns2-value", ns2Value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,11 +43,11 @@ import org.jivesoftware.smack.filter.OrFilter;
|
||||||
import org.jivesoftware.smack.filter.StanzaTypeFilter;
|
import org.jivesoftware.smack.filter.StanzaTypeFilter;
|
||||||
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler;
|
||||||
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
import org.jivesoftware.smack.iqrequest.IQRequestHandler.Mode;
|
||||||
import org.jivesoftware.smack.packet.DefaultExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.IQ;
|
import org.jivesoftware.smack.packet.IQ;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
|
import org.jivesoftware.smack.packet.StandardExtensionElement;
|
||||||
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
import org.jivesoftware.smackx.muc.packet.MUCUser;
|
||||||
import org.jivesoftware.smackx.search.ReportedData;
|
import org.jivesoftware.smackx.search.ReportedData;
|
||||||
import org.jivesoftware.smackx.workgroup.MetaData;
|
import org.jivesoftware.smackx.workgroup.MetaData;
|
||||||
|
@ -332,7 +332,7 @@ public class AgentSession {
|
||||||
if (online) {
|
if (online) {
|
||||||
presence = new Presence(Presence.Type.available);
|
presence = new Presence(Presence.Type.available);
|
||||||
presence.setTo(workgroupJID);
|
presence.setTo(workgroupJID);
|
||||||
presence.addExtension(new DefaultExtensionElement(AgentStatus.ELEMENT_NAME,
|
presence.addExtension(new StandardExtensionElement(AgentStatus.ELEMENT_NAME,
|
||||||
AgentStatus.NAMESPACE));
|
AgentStatus.NAMESPACE));
|
||||||
|
|
||||||
PacketCollector collector = this.connection.createPacketCollectorAndSend(new AndFilter(
|
PacketCollector collector = this.connection.createPacketCollectorAndSend(new AndFilter(
|
||||||
|
@ -350,7 +350,7 @@ public class AgentSession {
|
||||||
|
|
||||||
presence = new Presence(Presence.Type.unavailable);
|
presence = new Presence(Presence.Type.unavailable);
|
||||||
presence.setTo(workgroupJID);
|
presence.setTo(workgroupJID);
|
||||||
presence.addExtension(new DefaultExtensionElement(AgentStatus.ELEMENT_NAME,
|
presence.addExtension(new StandardExtensionElement(AgentStatus.ELEMENT_NAME,
|
||||||
AgentStatus.NAMESPACE));
|
AgentStatus.NAMESPACE));
|
||||||
connection.sendStanza(presence);
|
connection.sendStanza(presence);
|
||||||
}
|
}
|
||||||
|
@ -429,11 +429,12 @@ public class AgentSession {
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
presence.setStatus(status);
|
presence.setStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send information about max chats and current chats as a packet extension.
|
// Send information about max chats and current chats as a packet extension.
|
||||||
DefaultExtensionElement agentStatus = new DefaultExtensionElement(AgentStatus.ELEMENT_NAME,
|
StandardExtensionElement.Builder builder = StandardExtensionElement.builder(AgentStatus.ELEMENT_NAME,
|
||||||
AgentStatus.NAMESPACE);
|
AgentStatus.NAMESPACE);
|
||||||
agentStatus.setValue("max-chats", "" + maxChats);
|
builder.addElement("max_chats", Integer.toString(maxChats));
|
||||||
presence.addExtension(agentStatus);
|
presence.addExtension(builder.build());
|
||||||
presence.addExtension(new MetaData(this.metaData));
|
presence.addExtension(new MetaData(this.metaData));
|
||||||
|
|
||||||
PacketCollector collector = this.connection.createPacketCollectorAndSend(new AndFilter(
|
PacketCollector collector = this.connection.createPacketCollectorAndSend(new AndFilter(
|
||||||
|
@ -780,10 +781,10 @@ public class AgentSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify agent packets gives an overview of agent activity in a queue.
|
// Notify agent packets gives an overview of agent activity in a queue.
|
||||||
DefaultExtensionElement notifyAgents = (DefaultExtensionElement)presence.getExtension("notify-agents", "http://jabber.org/protocol/workgroup");
|
StandardExtensionElement notifyAgents = presence.getExtension("notify-agents", "http://jabber.org/protocol/workgroup");
|
||||||
if (notifyAgents != null) {
|
if (notifyAgents != null) {
|
||||||
int currentChats = Integer.parseInt(notifyAgents.getValue("current-chats"));
|
int currentChats = Integer.parseInt(notifyAgents.getFirstElement("current-chats", "http://jabber.org/protocol/workgroup").getText());
|
||||||
int maxChats = Integer.parseInt(notifyAgents.getValue("max-chats"));
|
int maxChats = Integer.parseInt(notifyAgents.getFirstElement("max-chats", "http://jabber.org/protocol/workgroup").getText());
|
||||||
queue.setCurrentChats(currentChats);
|
queue.setCurrentChats(currentChats);
|
||||||
queue.setMaxChats(maxChats);
|
queue.setMaxChats(maxChats);
|
||||||
// Fire event.
|
// Fire event.
|
||||||
|
|
Loading…
Reference in a new issue