001/** 002 * 003 * Copyright © 2014-2018 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.SmackException; 023import org.jivesoftware.smack.packet.ExtensionElement; 024import org.jivesoftware.smack.packet.IQ; 025import org.jivesoftware.smack.util.ParserUtils; 026 027import org.xmlpull.v1.XmlPullParser; 028import org.xmlpull.v1.XmlPullParserException; 029 030public class IntrospectionProvider{ 031 032 // Unfortunately, we have to create two introspection providers, with the exactly the same code here 033 034 public abstract static class IQIntrospectionProvider<I extends IQ> extends IQProvider<I> { 035 private final Class<I> elementClass; 036 037 protected IQIntrospectionProvider(Class<I> elementClass) { 038 this.elementClass = elementClass; 039 } 040 041 @SuppressWarnings("unchecked") 042 @Override 043 public I parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, 044 SmackException { 045 try { 046 return (I) parseWithIntrospection(elementClass, parser, initialDepth); 047 } 048 catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException 049 | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) { 050 throw new SmackException(e); 051 } 052 } 053 } 054 055 public abstract static class PacketExtensionIntrospectionProvider<PE extends ExtensionElement> extends ExtensionElementProvider<PE> { 056 private final Class<PE> elementClass; 057 058 protected PacketExtensionIntrospectionProvider(Class<PE> elementClass) { 059 this.elementClass = elementClass; 060 } 061 062 @SuppressWarnings("unchecked") 063 @Override 064 public PE parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, 065 SmackException { 066 try { 067 return (PE) parseWithIntrospection(elementClass, parser, initialDepth); 068 } 069 catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException 070 | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) { 071 throw new SmackException(e); 072 } 073 } 074 } 075 076 public static Object parseWithIntrospection(Class<?> objectClass, 077 XmlPullParser parser, final int initialDepth) throws NoSuchMethodException, SecurityException, 078 InstantiationException, IllegalAccessException, XmlPullParserException, 079 IOException, IllegalArgumentException, InvocationTargetException, 080 ClassNotFoundException { 081 ParserUtils.assertAtStartTag(parser); 082 Object object = objectClass.getConstructor().newInstance(); 083 outerloop: while (true) { 084 int eventType = parser.next(); 085 switch (eventType) { 086 case XmlPullParser.START_TAG: 087 String name = parser.getName(); 088 String stringValue = parser.nextText(); 089 Class<?> propertyType = object.getClass().getMethod( 090 "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType(); 091 // Get the value of the property by converting it from a 092 // String to the correct object type. 093 Object value = decode(propertyType, stringValue); 094 // Set the value of the bean. 095 object.getClass().getMethod( 096 "set" + Character.toUpperCase(name.charAt(0)) + name.substring(1), 097 propertyType).invoke(object, value); 098 break; 099 100 case XmlPullParser.END_TAG: 101 if (parser.getDepth() == initialDepth) { 102 break outerloop; 103 } 104 break; 105 } 106 } 107 ParserUtils.assertAtEndTag(parser); 108 return object; 109 } 110 111 /** 112 * Decodes a String into an object of the specified type. If the object 113 * type is not supported, null will be returned. 114 * 115 * @param type the type of the property. 116 * @param value the encode String value to decode. 117 * @return the String value decoded into the specified type. 118 * @throws ClassNotFoundException 119 */ 120 private static Object decode(Class<?> type, String value) throws ClassNotFoundException { 121 String name = type.getName(); 122 switch (name) { 123 case "java.lang.String": 124 return value; 125 case "boolean": 126 // CHECKSTYLE:OFF 127 return Boolean.valueOf(value); 128 // CHECKSTYLE:ON 129 case "int": 130 return Integer.valueOf(value); 131 case "long": 132 return Long.valueOf(value); 133 case "float": 134 return Float.valueOf(value); 135 case "double": 136 return Double.valueOf(value); 137 case "short": 138 return Short.valueOf(value); 139 case "byte": 140 return Byte.valueOf(value); 141 case "java.lang.Class": 142 return Class.forName(value); 143 } 144 return null; 145 } 146}