IntrospectionProvider.java

  1. /**
  2.  *
  3.  * Copyright © 2014-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.provider;

  18. import java.io.IOException;
  19. import java.lang.reflect.InvocationTargetException;

  20. import org.jivesoftware.smack.packet.ExtensionElement;
  21. import org.jivesoftware.smack.packet.IQ;
  22. import org.jivesoftware.smack.packet.IqData;
  23. import org.jivesoftware.smack.packet.XmlEnvironment;
  24. import org.jivesoftware.smack.util.ParserUtils;
  25. import org.jivesoftware.smack.xml.XmlPullParser;
  26. import org.jivesoftware.smack.xml.XmlPullParserException;

  27. /**
  28.  * Parsing with introspection poses a security threat and results in mutable classes and is therefore discouraged.
  29.  * @deprecated use a proper parser.
  30.  */
  31. // TODO: Remove in Smack 4.6.
  32. @Deprecated
  33. public class IntrospectionProvider{

  34.     // Unfortunately, we have to create two introspection providers, with the exactly the same code here

  35.     /**
  36.      * Parsing with introspection poses a security threat and results in mutable classes and is therefore discouraged.
  37.      * @deprecated use a proper parser.
  38.      */
  39.     // TODO: Remove in Smack 4.6.
  40.     @Deprecated
  41.     public abstract static class IQIntrospectionProvider<I extends IQ> extends IqProvider<I> {
  42.         private final Class<I> elementClass;

  43.         protected IQIntrospectionProvider(Class<I> elementClass) {
  44.             this.elementClass = elementClass;
  45.         }

  46.         @SuppressWarnings("unchecked")
  47.         @Override
  48.         public I parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
  49.             try {
  50.                 return (I) parseWithIntrospection(elementClass, parser, initialDepth);
  51.             }
  52.             catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
  53.                             | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) {
  54.                 // TODO: Should probably be SmackParsingException (once it exists).
  55.                 throw new IOException(e);
  56.             }
  57.         }
  58.     }

  59.     /**
  60.      * Parsing with introspection poses a security threat and results in mutable classes and is therefore discouraged.
  61.      * @deprecated use a proper parser.
  62.      */
  63.     // TODO: Remove in Smack 4.6.
  64.     @Deprecated
  65.     public abstract static class PacketExtensionIntrospectionProvider<PE extends ExtensionElement> extends ExtensionElementProvider<PE> {
  66.         private final Class<PE> elementClass;

  67.         protected PacketExtensionIntrospectionProvider(Class<PE> elementClass) {
  68.             this.elementClass = elementClass;
  69.         }

  70.         @SuppressWarnings("unchecked")
  71.         @Override
  72.         public PE parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException {
  73.             try {
  74.                 return (PE) parseWithIntrospection(elementClass, parser, initialDepth);
  75.             }
  76.             catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
  77.                             | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) {
  78.                 // TODO: Should probably be SmackParsingException (once it exists).
  79.                 throw new IOException(e);
  80.             }
  81.         }
  82.     }

  83.     public static Object parseWithIntrospection(Class<?> objectClass,
  84.                     XmlPullParser parser, final int initialDepth) throws NoSuchMethodException, SecurityException,
  85.                     InstantiationException, IllegalAccessException, XmlPullParserException,
  86.                     IOException, IllegalArgumentException, InvocationTargetException,
  87.                     ClassNotFoundException {
  88.         ParserUtils.assertAtStartTag(parser);
  89.         Object object = objectClass.getConstructor().newInstance();
  90.         outerloop: while (true) {
  91.             XmlPullParser.Event eventType = parser.next();
  92.             switch (eventType) {
  93.             case START_ELEMENT:
  94.                 String name = parser.getName();
  95.                 String stringValue = parser.nextText();
  96.                 Class<?> propertyType = object.getClass().getMethod(
  97.                                 "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType();
  98.                 // Get the value of the property by converting it from a
  99.                 // String to the correct object type.
  100.                 Object value = decode(propertyType, stringValue);
  101.                 // Set the value of the bean.
  102.                 object.getClass().getMethod(
  103.                                 "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1),
  104.                                 propertyType).invoke(object, value);
  105.                 break;

  106.             case  END_ELEMENT:
  107.                 if (parser.getDepth() == initialDepth) {
  108.                     break outerloop;
  109.                 }
  110.                 break;
  111.             default:
  112.                 // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement.
  113.                 break;
  114.             }
  115.         }
  116.         ParserUtils.assertAtEndTag(parser);
  117.         return object;
  118.     }

  119.     /**
  120.      * Decodes a String into an object of the specified type. If the object
  121.      * type is not supported, null will be returned.
  122.      *
  123.      * @param type the type of the property.
  124.      * @param value the encode String value to decode.
  125.      * @return the String value decoded into the specified type.
  126.      * @throws ClassNotFoundException if the provided class was not found.
  127.      */
  128.     private static Object decode(Class<?> type, String value) throws ClassNotFoundException {
  129.         String name = type.getName();
  130.         switch (name) {
  131.         case "java.lang.String":
  132.             return value;
  133.         case "boolean":
  134.             // CHECKSTYLE:OFF
  135.             return Boolean.valueOf(value);
  136.             // CHECKSTYLE:ON
  137.         case "int":
  138.             return Integer.valueOf(value);
  139.         case "long":
  140.             return Long.valueOf(value);
  141.         case "float":
  142.             return Float.valueOf(value);
  143.         case "double":
  144.             return Double.valueOf(value);
  145.         case "short":
  146.             return Short.valueOf(value);
  147.         case "byte":
  148.             return Byte.valueOf(value);
  149.         case "java.lang.Class":
  150.             return Class.forName(value);
  151.         }
  152.         return null;
  153.     }
  154. }