XmppElementUtil.java

  1. /**
  2.  *
  3.  * Copyright 2018-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.util;

  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.List;
  21. import java.util.logging.Logger;

  22. import javax.xml.namespace.QName;

  23. import org.jivesoftware.smack.packet.ExtensionElement;
  24. import org.jivesoftware.smack.packet.StandardExtensionElement;
  25. import org.jivesoftware.smack.packet.XmlElement;
  26. import org.jivesoftware.smack.provider.ProviderManager;

  27. import org.jxmpp.util.cache.LruCache;

  28. public class XmppElementUtil {

  29.     private static final LruCache<Class<? extends XmlElement>, QName> CLASS_TO_QNAME_CACHE = new LruCache<>(512);

  30.     public static final Logger LOGGER = Logger.getLogger(XmppElementUtil.class.getName());

  31.     public static QName getQNameFor(Class<? extends XmlElement> fullyQualifiedElement) {
  32.         QName qname = CLASS_TO_QNAME_CACHE.get(fullyQualifiedElement);
  33.         if (qname != null) {
  34.             return qname;
  35.         }

  36.         try {
  37.             Object qnameObject = fullyQualifiedElement.getField("QNAME").get(null);
  38.             if (QName.class.isAssignableFrom(qnameObject.getClass())) {
  39.                 qname = (QName) qnameObject;
  40.                 CLASS_TO_QNAME_CACHE.put(fullyQualifiedElement, qname);
  41.                 return qname;
  42.             }
  43.             LOGGER.warning("The QNAME field of " + fullyQualifiedElement + " is not of type QNAME.");
  44.         } catch (NoSuchFieldException e) {
  45.             LOGGER.finer("The " + fullyQualifiedElement + " has no static QNAME field. Consider adding one.");
  46.             // Proceed to fallback strategy.
  47.         } catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
  48.             throw new IllegalArgumentException(e);
  49.         }

  50.         String element, namespace;
  51.         try {
  52.             element = (String) fullyQualifiedElement.getField("ELEMENT").get(null);
  53.             namespace = (String) fullyQualifiedElement.getField("NAMESPACE").get(null);
  54.         }
  55.         catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
  56.             throw new IllegalArgumentException("The " + fullyQualifiedElement + " has no ELEMENT, NAMESPACE or QNAME member. Consider adding QNAME", e);
  57.         }

  58.         qname = new QName(namespace, element);
  59.         CLASS_TO_QNAME_CACHE.put(fullyQualifiedElement, qname);
  60.         return qname;
  61.     }

  62.     public static <E extends ExtensionElement> List<E> getElementsFrom(
  63.                     MultiMap<QName, XmlElement> elementMap, Class<E> extensionElementClass) {
  64.         QName qname = XmppElementUtil.getQNameFor(extensionElementClass);

  65.         List<XmlElement> extensionElements = elementMap.getAll(qname);

  66.         if (extensionElements.isEmpty()) {
  67.             return Collections.emptyList();
  68.         }

  69.         List<E> res = new ArrayList<>(extensionElements.size());
  70.         for (XmlElement extensionElement : extensionElements) {
  71.             E e = castOrThrow(extensionElement, extensionElementClass);
  72.             res.add(e);
  73.         }
  74.         return res;
  75.     }

  76.     public static <E extends ExtensionElement> E castOrThrow(XmlElement extensionElement, Class<E> extensionElementClass) {
  77.         if (!extensionElementClass.isInstance(extensionElement)) {
  78.             final QName qname = getQNameFor(extensionElementClass);

  79.             final String detailMessage;
  80.             if (extensionElement instanceof StandardExtensionElement) {
  81.                 detailMessage = "because there is no according extension element provider registered with ProviderManager for "
  82.                                 + qname
  83.                                 + ". WARNING: This indicates a serious problem with your Smack setup, probably causing Smack not being able to properly initialize itself.";
  84.             } else {
  85.                 Object provider = ProviderManager.getExtensionProvider(qname);
  86.                 detailMessage = "because there is an inconsistency with the provider registered with ProviderManager: the active provider for "
  87.                                 + qname + " '" + provider.getClass()
  88.                                 + "' does not return instances of type " + extensionElementClass
  89.                                 + ", but instead returns instances of type " + extensionElement.getClass() + ".";
  90.             }

  91.             String message = "Extension element is not of expected class '" + extensionElementClass.getName() + "', "
  92.                             + detailMessage;
  93.             throw new IllegalStateException(message);
  94.         }

  95.         return extensionElementClass.cast(extensionElement);
  96.     }

  97. }