1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-15 08:04:49 +02:00
Smack/smack-core/src/main/java/org/jivesoftware/smack/util/XmppElementUtil.java
Florian Schmaus 3d4e7938a7 Make ExtensionElement marker interface wrt. QNAME field
ExtensionElement is now a marker interface that requires all
implementation non-abstract classes to carry a static final QNAME
field (of type QName). This is verified by a new unit test.

Also FullyQualifiedElement is renamed to simply XmlElement. XmlElement
is used over ExtensionElement when implementing classes do not
statically know the qualified name of the XML elements they
represent. In general, XmlElement should be used sparingly, and every
XML element should be modeled by its own Java class (implementing
ExtensionElement).
2021-04-18 21:07:19 +02:00

118 lines
5.1 KiB
Java

/**
*
* Copyright 2018-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.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.XmlElement;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jxmpp.util.cache.LruCache;
public class XmppElementUtil {
private static final LruCache<Class<? extends XmlElement>, QName> CLASS_TO_QNAME_CACHE = new LruCache<>(512);
public static final Logger LOGGER = Logger.getLogger(XmppElementUtil.class.getName());
public static QName getQNameFor(Class<? extends XmlElement> fullyQualifiedElement) {
QName qname = CLASS_TO_QNAME_CACHE.get(fullyQualifiedElement);
if (qname != null) {
return qname;
}
try {
Object qnameObject = fullyQualifiedElement.getField("QNAME").get(null);
if (QName.class.isAssignableFrom(qnameObject.getClass())) {
qname = (QName) qnameObject;
CLASS_TO_QNAME_CACHE.put(fullyQualifiedElement, qname);
return qname;
}
LOGGER.warning("The QNAME field of " + fullyQualifiedElement + " is not of type QNAME.");
} catch (NoSuchFieldException e) {
LOGGER.finer("The " + fullyQualifiedElement + " has no static QNAME field. Consider adding one.");
// Proceed to fallback strategy.
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
throw new IllegalArgumentException(e);
}
String element, namespace;
try {
element = (String) fullyQualifiedElement.getField("ELEMENT").get(null);
namespace = (String) fullyQualifiedElement.getField("NAMESPACE").get(null);
}
catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
throw new IllegalArgumentException("The " + fullyQualifiedElement + " has no ELEMENT, NAMESPACE or QNAME member. Consider adding QNAME", e);
}
qname = new QName(namespace, element);
CLASS_TO_QNAME_CACHE.put(fullyQualifiedElement, qname);
return qname;
}
public static <E extends ExtensionElement> List<E> getElementsFrom(
MultiMap<QName, XmlElement> elementMap, Class<E> extensionElementClass) {
QName qname = XmppElementUtil.getQNameFor(extensionElementClass);
List<XmlElement> extensionElements = elementMap.getAll(qname);
if (extensionElements.isEmpty()) {
return Collections.emptyList();
}
List<E> res = new ArrayList<>(extensionElements.size());
for (XmlElement extensionElement : extensionElements) {
E e = castOrThrow(extensionElement, extensionElementClass);
res.add(e);
}
return res;
}
public static <E extends ExtensionElement> E castOrThrow(XmlElement extensionElement, Class<E> extensionElementClass) {
if (!extensionElementClass.isInstance(extensionElement)) {
final QName qname = getQNameFor(extensionElementClass);
final String detailMessage;
if (extensionElement instanceof StandardExtensionElement) {
detailMessage = "because there is no according extension element provider registered with ProviderManager for "
+ qname
+ ". WARNING: This indicates a serious problem with your Smack setup, probably causing Smack not being able to properly initialize itself.";
} else {
Object provider = ProviderManager.getExtensionProvider(qname);
detailMessage = "because there is an inconsistency with the provider registered with ProviderManager: the active provider for "
+ qname + " '" + provider.getClass()
+ "' does not return instances of type " + extensionElementClass
+ ", but instead returns instances of type " + extensionElement.getClass() + ".";
}
String message = "Extension element is not of expected class '" + extensionElementClass.getName() + "', "
+ detailMessage;
throw new IllegalStateException(message);
}
return extensionElementClass.cast(extensionElement);
}
}