org.freedesktop.dbus.Marshalling Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dbus-java Show documentation
Show all versions of dbus-java Show documentation
Improved version of the DBus-Java library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/).
/*
D-Bus Java Implementation
Copyright (c) 2005-2006 Matthew Johnson
This program is free software; you can redistribute it and/or modify it
under the terms of either the GNU Lesser General Public License Version 2 or the
Academic Free Licence Version 2.1.
Full licence texts are included in the COPYING file with this program.
*/
package org.freedesktop.dbus;
import static org.freedesktop.dbus.Gettext.t;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.types.DBusListType;
import org.freedesktop.dbus.types.DBusMapType;
import org.freedesktop.dbus.types.DBusStructType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Contains static methods for marshalling values.
*/
public final class Marshalling {
private static final Logger LOGGER = LoggerFactory.getLogger(Marshalling.class);
private static Map typeCache = new HashMap();
private Marshalling() {
}
/**
* Will return the DBus type corresponding to the given Java type.
* Note, container type should have their ParameterizedType not their
* Class passed in here.
* @param c The Java types.
* @return The DBus types.
* @throws DBusException If the given type cannot be converted to a DBus type.
*/
public static String getDBusType(Type[] c) throws DBusException {
StringBuffer sb = new StringBuffer();
for (Type t : c) {
for (String s : getDBusType(t)) {
sb.append(s);
}
}
return sb.toString();
}
/**
* Will return the DBus type corresponding to the given Java type.
* Note, container type should have their ParameterizedType not their
* Class passed in here.
* @param c The Java type.
* @return The DBus type.
* @throws DBusException If the given type cannot be converted to a DBus type.
*/
public static String[] getDBusType(Type c) throws DBusException {
String[] cached = typeCache.get(c);
if (null != cached) {
return cached;
}
cached = getDBusType(c, false);
typeCache.put(c, cached);
return cached;
}
/**
* Will return the DBus type corresponding to the given Java type.
* Note, container type should have their ParameterizedType not their
* Class passed in here.
* @param c The Java type.
* @param basic If true enforces this to be a non-compound type. (compound types are Maps, Structs and Lists/arrays).
* @return The DBus type.
* @throws DBusException If the given type cannot be converted to a DBus type.
*/
public static String[] getDBusType(Type c, boolean basic) throws DBusException {
return recursiveGetDBusType(c, basic, 0);
}
private static StringBuffer[] out = new StringBuffer[10];
@SuppressWarnings("unchecked")
public static String[] recursiveGetDBusType(Type c, boolean basic, int level) throws DBusException {
if (out.length <= level) {
StringBuffer[] newout = new StringBuffer[out.length];
System.arraycopy(out, 0, newout, 0, out.length);
out = newout;
}
if (null == out[level]) {
out[level] = new StringBuffer();
} else {
out[level].delete(0, out[level].length());
}
if (basic && !(c instanceof Class)) {
throw new DBusException(c + t(" is not a basic type"));
}
if (c instanceof TypeVariable) {
out[level].append((char) Message.ArgumentType.VARIANT);
} else if (c instanceof GenericArrayType) {
out[level].append((char) Message.ArgumentType.ARRAY);
String[] s = recursiveGetDBusType(((GenericArrayType) c).getGenericComponentType(), false, level + 1);
if (s.length != 1) {
throw new DBusException(t("Multi-valued array types not permitted"));
}
out[level].append(s[0]);
} else if ((c instanceof Class && DBusSerializable.class.isAssignableFrom((Class extends Object>) c)) || (c instanceof ParameterizedType && DBusSerializable.class.isAssignableFrom((Class extends Object>) ((ParameterizedType) c).getRawType()))) {
// it's a custom serializable type
Type[] newtypes = null;
if (c instanceof Class) {
for (Method m : ((Class extends Object>) c).getDeclaredMethods()) {
if (m.getName().equals("deserialize")) {
newtypes = m.getGenericParameterTypes();
}
}
} else {
for (Method m : ((Class extends Object>) ((ParameterizedType) c).getRawType()).getDeclaredMethods()) {
if (m.getName().equals("deserialize")) {
newtypes = m.getGenericParameterTypes();
}
}
}
if (null == newtypes) {
throw new DBusException(t("Serializable classes must implement a deserialize method"));
}
String[] sigs = new String[newtypes.length];
for (int j = 0; j < sigs.length; j++) {
String[] ss = recursiveGetDBusType(newtypes[j], false, level + 1);
if (1 != ss.length) {
throw new DBusException(t("Serializable classes must serialize to native DBus types"));
}
sigs[j] = ss[0];
}
return sigs;
} else if (c instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) c;
if (p.getRawType().equals(Map.class)) {
out[level].append("a{");
Type[] t = p.getActualTypeArguments();
try {
String[] s = recursiveGetDBusType(t[0], true, level + 1);
if (s.length != 1) {
throw new DBusException(t("Multi-valued array types not permitted"));
}
out[level].append(s[0]);
s = recursiveGetDBusType(t[1], false, level + 1);
if (s.length != 1) {
throw new DBusException(t("Multi-valued array types not permitted"));
}
out[level].append(s[0]);
} catch (ArrayIndexOutOfBoundsException aioobe) {
if (AbstractConnection.EXCEPTION_DEBUG) {
LOGGER.error("", aioobe);
}
throw new DBusException(t("Map must have 2 parameters"));
}
out[level].append('}');
} else if (List.class.isAssignableFrom((Class extends Object>) p.getRawType())) {
for (Type t : p.getActualTypeArguments()) {
if (Type.class.equals(t)) {
out[level].append((char) Message.ArgumentType.SIGNATURE);
} else {
String[] s = recursiveGetDBusType(t, false, level + 1);
if (s.length != 1) {
throw new DBusException(t("Multi-valued array types not permitted"));
}
out[level].append((char) Message.ArgumentType.ARRAY);
out[level].append(s[0]);
}
}
} else if (p.getRawType().equals(Variant.class)) {
out[level].append((char) Message.ArgumentType.VARIANT);
} else if (DBusInterface.class.isAssignableFrom((Class extends Object>) p.getRawType())) {
out[level].append((char) Message.ArgumentType.OBJECT_PATH);
} else if (Tuple.class.isAssignableFrom((Class extends Object>) p.getRawType())) {
Type[] ts = p.getActualTypeArguments();
Vector vs = new Vector();
for (Type t : ts) {
for (String s : recursiveGetDBusType(t, false, level + 1)) {
vs.add(s);
}
}
return vs.toArray(new String[0]);
} else {
throw new DBusException(t("Exporting non-exportable parameterized type ") + c);
}
}
else if (c.equals(Byte.class)) {
out[level].append((char) Message.ArgumentType.BYTE);
} else if (c.equals(Byte.TYPE)) {
out[level].append((char) Message.ArgumentType.BYTE);
} else if (c.equals(Boolean.class)) {
out[level].append((char) Message.ArgumentType.BOOLEAN);
} else if (c.equals(Boolean.TYPE)) {
out[level].append((char) Message.ArgumentType.BOOLEAN);
} else if (c.equals(Short.class)) {
out[level].append((char) Message.ArgumentType.INT16);
} else if (c.equals(Short.TYPE)) {
out[level].append((char) Message.ArgumentType.INT16);
} else if (c.equals(UInt16.class)) {
out[level].append((char) Message.ArgumentType.UINT16);
} else if (c.equals(Integer.class)) {
out[level].append((char) Message.ArgumentType.INT32);
} else if (c.equals(Integer.TYPE)) {
out[level].append((char) Message.ArgumentType.INT32);
} else if (c.equals(UInt32.class)) {
out[level].append((char) Message.ArgumentType.UINT32);
} else if (c.equals(Long.class)) {
out[level].append((char) Message.ArgumentType.INT64);
} else if (c.equals(Long.TYPE)) {
out[level].append((char) Message.ArgumentType.INT64);
} else if (c.equals(UInt64.class)) {
out[level].append((char) Message.ArgumentType.UINT64);
} else if (c.equals(Double.class)) {
out[level].append((char) Message.ArgumentType.DOUBLE);
} else if (c.equals(Double.TYPE)) {
out[level].append((char) Message.ArgumentType.DOUBLE);
} else if (c.equals(Float.class) && AbstractConnection.FLOAT_SUPPORT) {
out[level].append((char) Message.ArgumentType.FLOAT);
} else if (c.equals(Float.class)) {
out[level].append((char) Message.ArgumentType.DOUBLE);
} else if (c.equals(Float.TYPE) && AbstractConnection.FLOAT_SUPPORT) {
out[level].append((char) Message.ArgumentType.FLOAT);
} else if (c.equals(Float.TYPE)) {
out[level].append((char) Message.ArgumentType.DOUBLE);
} else if (c.equals(String.class)) {
out[level].append((char) Message.ArgumentType.STRING);
} else if (c.equals(Variant.class)) {
out[level].append((char) Message.ArgumentType.VARIANT);
} else if (c instanceof Class && DBusInterface.class.isAssignableFrom((Class extends Object>) c)) {
out[level].append((char) Message.ArgumentType.OBJECT_PATH);
} else if (c instanceof Class && Path.class.equals(c)) {
out[level].append((char) Message.ArgumentType.OBJECT_PATH);
} else if (c instanceof Class && ObjectPath.class.equals(c)) {
out[level].append((char) Message.ArgumentType.OBJECT_PATH);
} else if (c instanceof Class && ((Class extends Object>) c).isArray()) {
if (Type.class.equals(((Class extends Object>) c).getComponentType())) {
out[level].append((char) Message.ArgumentType.SIGNATURE);
} else {
out[level].append((char) Message.ArgumentType.ARRAY);
String[] s = recursiveGetDBusType(((Class extends Object>) c).getComponentType(), false, level + 1);
if (s.length != 1) {
throw new DBusException(t("Multi-valued array types not permitted"));
}
out[level].append(s[0]);
}
} else if (c instanceof Class && Struct.class.isAssignableFrom((Class extends Object>) c)) {
out[level].append((char) Message.ArgumentType.STRUCT1);
Type[] ts = Container.getTypeCache(c);
if (null == ts) {
Field[] fs = ((Class extends Object>) c).getDeclaredFields();
ts = new Type[fs.length];
for (Field f : fs) {
Position p = f.getAnnotation(Position.class);
if (null == p) {
continue;
}
ts[p.value()] = f.getGenericType();
}
Container.putTypeCache(c, ts);
}
for (Type t : ts) {
if (t != null) {
for (String s : recursiveGetDBusType(t, false, level + 1)) {
out[level].append(s);
}
}
}
out[level].append(')');
} else {
throw new DBusException(t("Exporting non-exportable type ") + c);
}
LOGGER.trace("Converted Java type: " + c + " to D-Bus Type: " + out[level]);
return new String[] {
out[level].toString()
};
}
/**
* Converts a dbus type string into Java Type objects,
* @param dbus The DBus type or types.
* @param rv Vector to return the types in.
* @param limit Maximum number of types to parse (-1 == nolimit).
* @return number of characters parsed from the type string.
*/
public static int getJavaType(String dbus, List rv, int limit) throws DBusException {
if (null == dbus || "".equals(dbus) || 0 == limit) {
return 0;
}
try {
int i = 0;
for (; i < dbus.length() && (-1 == limit || limit > rv.size()); i++) {
switch (dbus.charAt(i)) {
case Message.ArgumentType.STRUCT1:
int j = i + 1;
for (int c = 1; c > 0; j++) {
if (')' == dbus.charAt(j)) {
c--;
} else if (Message.ArgumentType.STRUCT1 == dbus.charAt(j)) {
c++;
}
}
Vector contained = new Vector();
int c = getJavaType(dbus.substring(i + 1, j - 1), contained, -1);
rv.add(new DBusStructType(contained.toArray(new Type[0])));
i = j;
break;
case Message.ArgumentType.ARRAY:
if (Message.ArgumentType.DICT_ENTRY1 == dbus.charAt(i + 1)) {
contained = new Vector();
c = getJavaType(dbus.substring(i + 2), contained, 2);
rv.add(new DBusMapType(contained.get(0), contained.get(1)));
i += (c + 2);
} else {
contained = new Vector();
c = getJavaType(dbus.substring(i + 1), contained, 1);
rv.add(new DBusListType(contained.get(0)));
i += c;
}
break;
case Message.ArgumentType.VARIANT:
rv.add(Variant.class);
break;
case Message.ArgumentType.BOOLEAN:
rv.add(Boolean.class);
break;
case Message.ArgumentType.INT16:
rv.add(Short.class);
break;
case Message.ArgumentType.BYTE:
rv.add(Byte.class);
break;
case Message.ArgumentType.OBJECT_PATH:
rv.add(DBusInterface.class);
break;
case Message.ArgumentType.UINT16:
rv.add(UInt16.class);
break;
case Message.ArgumentType.INT32:
rv.add(Integer.class);
break;
case Message.ArgumentType.UINT32:
rv.add(UInt32.class);
break;
case Message.ArgumentType.INT64:
rv.add(Long.class);
break;
case Message.ArgumentType.UINT64:
rv.add(UInt64.class);
break;
case Message.ArgumentType.DOUBLE:
rv.add(Double.class);
break;
case Message.ArgumentType.FLOAT:
rv.add(Float.class);
break;
case Message.ArgumentType.STRING:
rv.add(String.class);
break;
case Message.ArgumentType.SIGNATURE:
rv.add(Type[].class);
break;
case Message.ArgumentType.DICT_ENTRY1:
rv.add(Map.Entry.class);
contained = new Vector();
c = getJavaType(dbus.substring(i + 1), contained, 2);
i += c + 1;
break;
default:
throw new DBusException(MessageFormat.format(t("Failed to parse DBus type signature: {0} ({1})."), new Object[] {
dbus, dbus.charAt(i)
}));
}
}
return i;
} catch (IndexOutOfBoundsException ioobe) {
if (AbstractConnection.EXCEPTION_DEBUG) {
LOGGER.error("", ioobe);
}
throw new DBusException(t("Failed to parse DBus type signature: ") + dbus);
}
}
/**
* Recursively converts types for serialization onto DBus.
* @param parameters The parameters to convert.
* @param types The (possibly generic) types of the parameters.
* @return The converted parameters.
* @throws DBusException Thrown if there is an error in converting the objects.
*/
public static Object[] convertParameters(Object[] parameters, Type[] types, AbstractConnection conn) throws DBusException {
if (null == parameters) {
return null;
}
for (int i = 0; i < parameters.length; i++) {
LOGGER.trace("Converting " + i + " from " + parameters[i] + " to " + types[i]);
if (null == parameters[i]) {
continue;
}
if (parameters[i] instanceof DBusSerializable) {
for (Method m : parameters[i].getClass().getDeclaredMethods()) {
if (m.getName().equals("deserialize")) {
Type[] newtypes = m.getParameterTypes();
Type[] expand = new Type[types.length + newtypes.length - 1];
System.arraycopy(types, 0, expand, 0, i);
System.arraycopy(newtypes, 0, expand, i, newtypes.length);
System.arraycopy(types, i + 1, expand, i + newtypes.length, types.length - i - 1);
types = expand;
Object[] newparams = ((DBusSerializable) parameters[i]).serialize();
Object[] exparams = new Object[parameters.length + newparams.length - 1];
System.arraycopy(parameters, 0, exparams, 0, i);
System.arraycopy(newparams, 0, exparams, i, newparams.length);
System.arraycopy(parameters, i + 1, exparams, i + newparams.length, parameters.length - i - 1);
parameters = exparams;
}
}
i--;
} else if (parameters[i] instanceof Tuple) {
Type[] newtypes = ((ParameterizedType) types[i]).getActualTypeArguments();
Type[] expand = new Type[types.length + newtypes.length - 1];
System.arraycopy(types, 0, expand, 0, i);
System.arraycopy(newtypes, 0, expand, i, newtypes.length);
System.arraycopy(types, i + 1, expand, i + newtypes.length, types.length - i - 1);
types = expand;
Object[] newparams = ((Tuple) parameters[i]).getParameters();
Object[] exparams = new Object[parameters.length + newparams.length - 1];
System.arraycopy(parameters, 0, exparams, 0, i);
System.arraycopy(newparams, 0, exparams, i, newparams.length);
System.arraycopy(parameters, i + 1, exparams, i + newparams.length, parameters.length - i - 1);
parameters = exparams;
LOGGER.trace("New params: " + Arrays.deepToString(parameters) + " new types: " + Arrays.deepToString(types));
i--;
} else if (types[i] instanceof TypeVariable && !(parameters[i] instanceof Variant)) {
// its an unwrapped variant, wrap it
parameters[i] = new Variant