1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2025-09-10 18:59:41 +02:00

Add (IQ|PacketExtension)IntrospectionProvider

This simplifies code as there is no longer a distinction between
"normal" providers and introspection providers in ProviderManager
necessary.

It's also easier to get an idea where introspection is used for parsing.
This commit is contained in:
Florian Schmaus 2014-12-10 11:06:04 +01:00
parent 00f5008794
commit 77f0fdc156
11 changed files with 284 additions and 212 deletions

View file

@ -38,14 +38,4 @@ public final class ExtensionProviderInfo extends AbstractProviderInfo {
super(elementName, namespace, extProvider);
}
/**
* Defines an extension provider which is adheres to the JavaBean spec for parsing the extension.
*
* @param elementName Element that provider parses.
* @param namespace Namespace that provider parses.
* @param beanClass The provider bean class.
*/
public ExtensionProviderInfo(String elementName, String namespace, Class<?> beanClass) {
super(elementName, namespace, beanClass);
}
}

View file

@ -37,15 +37,4 @@ public final class IQProviderInfo extends AbstractProviderInfo {
public IQProviderInfo(String elementName, String namespace, IQProvider<IQ> iqProvider) {
super(elementName, namespace, iqProvider);
}
/**
* Defines an IQ class which can be used as a provider via introspection.
*
* @param elementName Element that provider parses.
* @param namespace Namespace that provider parses.
* @param iqProviderClass The IQ class being parsed.
*/
public IQProviderInfo(String elementName, String namespace, Class<? extends IQ> iqProviderClass) {
super(elementName, namespace, iqProviderClass);
}
}

View file

@ -0,0 +1,143 @@
/**
*
* Copyright © 2014 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.provider;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.util.ParserUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
public class IntrospectionProvider{
// Unfortunately, we have to create two introspection providers, with the exactly the same code here
public static abstract class IQIntrospectionProvider<I extends IQ> extends IQProvider<I> {
private final Class<I> elementClass;
protected IQIntrospectionProvider(Class<I> elementClass) {
this.elementClass = elementClass;
}
@SuppressWarnings("unchecked")
@Override
public I parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException,
SmackException {
try {
return (I) parseWithIntrospection(elementClass, parser, initialDepth);
}
catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) {
throw new SmackException(e);
}
}
}
public static abstract class PacketExtensionIntrospectionProvider<PE extends PacketExtension> extends PacketExtensionProvider<PE> {
private final Class<PE> elementClass;
protected PacketExtensionIntrospectionProvider(Class<PE> elementClass) {
this.elementClass = elementClass;
}
@SuppressWarnings("unchecked")
@Override
public PE parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException,
SmackException {
try {
return (PE) parseWithIntrospection(elementClass, parser, initialDepth);
}
catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) {
throw new SmackException(e);
}
}
}
public static Object parseWithIntrospection(Class<?> objectClass,
XmlPullParser parser, final int initialDepth) throws NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException, XmlPullParserException,
IOException, IllegalArgumentException, InvocationTargetException,
ClassNotFoundException {
ParserUtils.assertAtStartTag(parser);
Object object = objectClass.newInstance();
outerloop: while (true) {
int eventType = parser.next();
switch (eventType) {
case XmlPullParser.START_TAG:
String name = parser.getName();
String stringValue = parser.nextText();
Class<?> propertyType = object.getClass().getMethod(
"get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType();
// Get the value of the property by converting it from a
// String to the correct object type.
Object value = decode(propertyType, stringValue);
// Set the value of the bean.
object.getClass().getMethod(
"set" + Character.toUpperCase(name.charAt(0)) + name.substring(1),
propertyType).invoke(object, value);
break;
case XmlPullParser.END_TAG:
if (parser.getDepth() == initialDepth) {
break outerloop;
}
break;
}
}
ParserUtils.assertAtEndTag(parser);
return object;
}
/**
* Decodes a String into an object of the specified type. If the object
* type is not supported, null will be returned.
*
* @param type the type of the property.
* @param value the encode String value to decode.
* @return the String value decoded into the specified type.
* @throws ClassNotFoundException
*/
private static Object decode(Class<?> type, String value) throws ClassNotFoundException {
if (type.getName().equals("java.lang.String")) {
return value;
}
if (type.getName().equals("boolean")) {
return Boolean.valueOf(value);
}
if (type.getName().equals("int")) {
return Integer.valueOf(value);
}
if (type.getName().equals("long")) {
return Long.valueOf(value);
}
if (type.getName().equals("float")) {
return Float.valueOf(value);
}
if (type.getName().equals("double")) {
return Double.valueOf(value);
}
if (type.getName().equals("java.lang.Class")) {
return Class.forName(value);
}
return null;
}
}

View file

@ -85,13 +85,8 @@ public class ProviderFileLoader implements ProviderLoader {
if (IQProvider.class.isAssignableFrom(provider)) {
iqProviders.add(new IQProviderInfo(elementName, namespace, (IQProvider<IQ>) provider.newInstance()));
}
else if (IQ.class.isAssignableFrom(provider)) {
iqProviders.add(new IQProviderInfo(elementName, namespace, (Class<? extends IQ>)provider));
}
else {
exceptions.add(new IllegalArgumentException(
className
+ " is neither IQProvider or IQ class"));
exceptions.add(new IllegalArgumentException(className + " is not a IQProvider"));
}
break;
case "extensionProvider":
@ -103,13 +98,9 @@ public class ProviderFileLoader implements ProviderLoader {
if (PacketExtensionProvider.class.isAssignableFrom(provider)) {
extProviders.add(new ExtensionProviderInfo(elementName, namespace, (PacketExtensionProvider<PacketExtension>) provider.newInstance()));
}
else if (PacketExtension.class.isAssignableFrom(provider)) {
extProviders.add(new ExtensionProviderInfo(elementName, namespace, provider));
}
else {
exceptions.add(new IllegalArgumentException(
className
+ " is neither PacketExtensionProvider or PacketExtension class"));
exceptions.add(new IllegalArgumentException(className
+ " is not a PacketExtensionProvider"));
}
break;
case "streamFeatureProvider":

View file

@ -18,7 +18,6 @@
package org.jivesoftware.smack.provider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -112,8 +111,6 @@ public final class ProviderManager {
private static final Map<String, PacketExtensionProvider<PacketExtension>> extensionProviders = new ConcurrentHashMap<String, PacketExtensionProvider<PacketExtension>>();
private static final Map<String, IQProvider<IQ>> iqProviders = new ConcurrentHashMap<String, IQProvider<IQ>>();
private static final Map<String, Class<?>> extensionIntrospectionProviders = new ConcurrentHashMap<String, Class<?>>();
private static final Map<String, Class<?>> iqIntrospectionProviders = new ConcurrentHashMap<String, Class<?>>();
private static final Map<String, PacketExtensionProvider<PacketExtension>> streamFeatureProviders = new ConcurrentHashMap<String, PacketExtensionProvider<PacketExtension>>();
static {
@ -171,11 +168,6 @@ public final class ProviderManager {
return iqProviders.get(key);
}
public static Class<?> getIQIntrospectionProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace);
return iqIntrospectionProviders.get(key);
}
/**
* Returns an unmodifiable collection of all IQProvider instances. Each object
* in the collection will either be an IQProvider instance, or a Class object
@ -183,11 +175,10 @@ public final class ProviderManager {
*
* @return all IQProvider instances.
*/
public static List<Object> getIQProviders() {
List<Object> providers = new ArrayList<Object>(iqProviders.size() + iqIntrospectionProviders.size());
public static List<IQProvider<IQ>> getIQProviders() {
List<IQProvider<IQ>> providers = new ArrayList<>(iqProviders.size());
providers.addAll(iqProviders.values());
providers.addAll(iqIntrospectionProviders.values());
return Collections.unmodifiableList(providers);
return providers;
}
/**
@ -208,11 +199,8 @@ public final class ProviderManager {
String key = removeIQProvider(elementName, namespace);
if (provider instanceof IQProvider) {
iqProviders.put(key, (IQProvider<IQ>) provider);
} else if (provider instanceof Class && IQ.class.isAssignableFrom((Class<?>) provider)) {
iqIntrospectionProviders.put(key, (Class<?>) provider);
} else {
throw new IllegalArgumentException("Provider must be an IQProvider " +
"or a Class instance sublcassing IQ.");
throw new IllegalArgumentException("Provider must be an IQProvider");
}
}
@ -228,7 +216,6 @@ public final class ProviderManager {
public static String removeIQProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace);
iqProviders.remove(key);
iqIntrospectionProviders.remove(key);
return key;
}
@ -256,11 +243,6 @@ public final class ProviderManager {
return extensionProviders.get(key);
}
public static Class<?> getExtensionIntrospectionProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace);
return extensionIntrospectionProviders.get(key);
}
/**
* Adds an extension provider with the specified element name and name space. The provider
* will override any providers loaded through the classpath. The provider must be either
@ -279,11 +261,8 @@ public final class ProviderManager {
String key = removeExtensionProvider(elementName, namespace);
if (provider instanceof PacketExtensionProvider) {
extensionProviders.put(key, (PacketExtensionProvider<PacketExtension>) provider);
} else if (provider instanceof Class && PacketExtension.class.isAssignableFrom((Class<?>) provider)) {
extensionIntrospectionProviders.put(key, (Class<?>) provider);
} else {
throw new IllegalArgumentException("Provider must be a PacketExtensionProvider " +
"or a Class instance.");
throw new IllegalArgumentException("Provider must be a PacketExtensionProvider");
}
}
@ -299,7 +278,6 @@ public final class ProviderManager {
public static String removeExtensionProvider(String elementName, String namespace) {
String key = getKey(elementName, namespace);
extensionProviders.remove(key);
extensionIntrospectionProviders.remove(key);
return key;
}
@ -310,11 +288,10 @@ public final class ProviderManager {
*
* @return all PacketExtensionProvider instances.
*/
public static List<Object> getExtensionProviders() {
List<Object> providers = new ArrayList<Object>(extensionProviders.size() + extensionIntrospectionProviders.size());
public static List<PacketExtensionProvider<PacketExtension>> getExtensionProviders() {
List<PacketExtensionProvider<PacketExtension>> providers = new ArrayList<>(extensionProviders.size());
providers.addAll(extensionProviders.values());
providers.addAll(extensionIntrospectionProviders.values());
return Collections.unmodifiableList(providers);
return providers;
}
public static PacketExtensionProvider<PacketExtension> getStreamFeatureProvider(String elementName, String namespace) {

View file

@ -19,7 +19,6 @@ package org.jivesoftware.smack.util;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@ -627,23 +626,15 @@ public class PacketParserUtils {
IQProvider<IQ> provider = ProviderManager.getIQProvider(elementName, namespace);
if (provider != null) {
iqPacket = provider.parse(parser);
} else {
Class<?> introspectionProvider = ProviderManager.getIQIntrospectionProvider(
elementName, namespace);
if (introspectionProvider != null) {
iqPacket = (IQ) PacketParserUtils.parseWithIntrospection(
elementName, introspectionProvider,
parser);
}
// Only handle unknown IQs of type result. Types of 'get' and 'set' which are not understood
// have to be answered with an IQ error response. See the code a few lines below
// Note that if we reach this code, it is guranteed that the result IQ contained a child element
// (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_TAG first.
else if (IQ.Type.result == type){
// No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
// so that the content of the IQ can be examined later on
iqPacket = new UnparsedResultIQ(elementName, namespace, parseElement(parser));
}
}
// Only handle unknown IQs of type result. Types of 'get' and 'set' which are not understood
// have to be answered with an IQ error response. See the code a few lines below
// Note that if we reach this code, it is guranteed that the result IQ contained a child element
// (RFC 6120 § 8.2.3 6) because otherwhise we would have reached the END_TAG first.
else if (IQ.Type.result == type) {
// No Provider found for the IQ stanza, parse it to an UnparsedIQ instance
// so that the content of the IQ can be examined later on
iqPacket = new UnparsedResultIQ(elementName, namespace, parseElement(parser));
}
break;
}
@ -931,18 +922,6 @@ public class PacketParserUtils {
if (provider != null) {
return provider.parse(parser);
}
Class<?> introspectionProvider = ProviderManager.getExtensionIntrospectionProvider(elementName, namespace);
if (introspectionProvider != null) {
try {
return (PacketExtension)parseWithIntrospection(elementName, introspectionProvider, parser);
} catch (NoSuchMethodException | SecurityException
| InstantiationException | IllegalAccessException
| IllegalArgumentException
| InvocationTargetException
| ClassNotFoundException e) {
throw new SmackException(e);
}
}
final int initialDepth = parser.getDepth();
// No providers registered, so use a default extension.
@ -1013,37 +992,6 @@ public class PacketParserUtils {
return null;
}
public static Object parseWithIntrospection(String elementName, Class<?> objectClass,
XmlPullParser parser) throws NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException, XmlPullParserException,
IOException, IllegalArgumentException, InvocationTargetException,
ClassNotFoundException {
boolean done = false;
Object object = objectClass.newInstance();
while (!done) {
int eventType = parser.next();
if (eventType == XmlPullParser.START_TAG) {
String name = parser.getName();
String stringValue = parser.nextText();
Class<?> propertyType = object.getClass().getMethod(
"get" + Character.toUpperCase(name.charAt(0)) + name.substring(1)).getReturnType();
// Get the value of the property by converting it from a
// String to the correct object type.
Object value = decode(propertyType, stringValue);
// Set the value of the bean.
object.getClass().getMethod(
"set" + Character.toUpperCase(name.charAt(0)) + name.substring(1),
propertyType).invoke(object, value);
}
else if (eventType == XmlPullParser.END_TAG) {
if (parser.getName().equals(elementName)) {
done = true;
}
}
}
return object;
}
public static void addPacketExtension(Packet packet, XmlPullParser parser) throws XmlPullParserException,
IOException, SmackException {
ParserUtils.assertAtStartTag(parser);
@ -1067,40 +1015,6 @@ public class PacketParserUtils {
collection.add(packetExtension);
}
/**
* Decodes a String into an object of the specified type. If the object
* type is not supported, null will be returned.
*
* @param type the type of the property.
* @param value the encode String value to decode.
* @return the String value decoded into the specified type.
* @throws ClassNotFoundException
*/
private static Object decode(Class<?> type, String value) throws ClassNotFoundException {
if (type.getName().equals("java.lang.String")) {
return value;
}
if (type.getName().equals("boolean")) {
return Boolean.valueOf(value);
}
if (type.getName().equals("int")) {
return Integer.valueOf(value);
}
if (type.getName().equals("long")) {
return Long.valueOf(value);
}
if (type.getName().equals("float")) {
return Float.valueOf(value);
}
if (type.getName().equals("double")) {
return Double.valueOf(value);
}
if (type.getName().equals("java.lang.Class")) {
return Class.forName(value);
}
return null;
}
/**
* This class represents and unparsed IQ of the type 'result'. Usually it's created when no IQProvider
* was found for the IQ element.