001/** 002 * 003 * Copyright © 2014-2021 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.provider; 018 019import java.io.IOException; 020import java.lang.reflect.InvocationTargetException; 021 022import org.jivesoftware.smack.packet.ExtensionElement; 023import org.jivesoftware.smack.packet.IQ; 024import org.jivesoftware.smack.packet.IqData; 025import org.jivesoftware.smack.packet.XmlEnvironment; 026import org.jivesoftware.smack.util.ParserUtils; 027import org.jivesoftware.smack.xml.XmlPullParser; 028import org.jivesoftware.smack.xml.XmlPullParserException; 029 030/** 031 * Parsing with introspection poses a security threat and results in mutable classes and is therefore discouraged. 032 * @deprecated use a proper parser. 033 */ 034// TODO: Remove in Smack 4.6. 035@Deprecated 036public class IntrospectionProvider{ 037 038 // Unfortunately, we have to create two introspection providers, with the exactly the same code here 039 040 /** 041 * Parsing with introspection poses a security threat and results in mutable classes and is therefore discouraged. 042 * @deprecated use a proper parser. 043 */ 044 // TODO: Remove in Smack 4.6. 045 @Deprecated 046 public abstract static class IQIntrospectionProvider<I extends IQ> extends IqProvider<I> { 047 private final Class<I> elementClass; 048 049 protected IQIntrospectionProvider(Class<I> elementClass) { 050 this.elementClass = elementClass; 051 } 052 053 @SuppressWarnings("unchecked") 054 @Override 055 public I parse(XmlPullParser parser, int initialDepth, IqData iqData, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException { 056 try { 057 return (I) parseWithIntrospection(elementClass, parser, initialDepth); 058 } 059 catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException 060 | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) { 061 // TODO: Should probably be SmackParsingException (once it exists). 062 throw new IOException(e); 063 } 064 } 065 } 066 067 /** 068 * Parsing with introspection poses a security threat and results in mutable classes and is therefore discouraged. 069 * @deprecated use a proper parser. 070 */ 071 // TODO: Remove in Smack 4.6. 072 @Deprecated 073 public abstract static class PacketExtensionIntrospectionProvider<PE extends ExtensionElement> extends ExtensionElementProvider<PE> { 074 private final Class<PE> elementClass; 075 076 protected PacketExtensionIntrospectionProvider(Class<PE> elementClass) { 077 this.elementClass = elementClass; 078 } 079 080 @SuppressWarnings("unchecked") 081 @Override 082 public PE parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) throws XmlPullParserException, IOException { 083 try { 084 return (PE) parseWithIntrospection(elementClass, parser, initialDepth); 085 } 086 catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException 087 | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) { 088 // TODO: Should probably be SmackParsingException (once it exists). 089 throw new IOException(e); 090 } 091 } 092 } 093 094 public static Object parseWithIntrospection(Class<?> objectClass, 095 XmlPullParser parser, final int initialDepth) throws NoSuchMethodException, SecurityException, 096 InstantiationException, IllegalAccessException, XmlPullParserException, 097 IOException, IllegalArgumentException, InvocationTargetException, 098 ClassNotFoundException { 099 ParserUtils.assertAtStartTag(parser); 100 Object object = objectClass.getConstructor().newInstance(); 101 outerloop: while (true) { 102 XmlPullParser.Event eventType = parser.next(); 103 switch (eventType) { 104 case START_ELEMENT: 105 String name = parser.getName(); 106 String stringValue = parser.nextText(); 107 Class<?> propertyType = object.getClass().getMethod( 108 "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType(); 109 // Get the value of the property by converting it from a 110 // String to the correct object type. 111 Object value = decode(propertyType, stringValue); 112 // Set the value of the bean. 113 object.getClass().getMethod( 114 "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1), 115 propertyType).invoke(object, value); 116 break; 117 118 case END_ELEMENT: 119 if (parser.getDepth() == initialDepth) { 120 break outerloop; 121 } 122 break; 123 default: 124 // Catch all for incomplete switch (MissingCasesInEnumSwitch) statement. 125 break; 126 } 127 } 128 ParserUtils.assertAtEndTag(parser); 129 return object; 130 } 131 132 /** 133 * Decodes a String into an object of the specified type. If the object 134 * type is not supported, null will be returned. 135 * 136 * @param type the type of the property. 137 * @param value the encode String value to decode. 138 * @return the String value decoded into the specified type. 139 * @throws ClassNotFoundException if the provided class was not found. 140 */ 141 private static Object decode(Class<?> type, String value) throws ClassNotFoundException { 142 String name = type.getName(); 143 switch (name) { 144 case "java.lang.String": 145 return value; 146 case "boolean": 147 // CHECKSTYLE:OFF 148 return Boolean.valueOf(value); 149 // CHECKSTYLE:ON 150 case "int": 151 return Integer.valueOf(value); 152 case "long": 153 return Long.valueOf(value); 154 case "float": 155 return Float.valueOf(value); 156 case "double": 157 return Double.valueOf(value); 158 case "short": 159 return Short.valueOf(value); 160 case "byte": 161 return Byte.valueOf(value); 162 case "java.lang.Class": 163 return Class.forName(value); 164 } 165 return null; 166 } 167}