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}