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:
parent
00f5008794
commit
77f0fdc156
11 changed files with 284 additions and 212 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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":
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue