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