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}