/* 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 org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusExecutionException; import java.lang.annotation.Annotation; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; import static org.freedesktop.dbus.Gettext.getString; class ExportedObject { @SuppressWarnings("unchecked") private String getAnnotations(AnnotatedElement c) { String ans = ""; for (Annotation a : c.getDeclaredAnnotations()) { Class t = a.annotationType(); String value = ""; try { Method m = t.getMethod("value"); value = m.invoke(a).toString(); } catch (NoSuchMethodException NSMe) { } catch (InvocationTargetException ITe) { } catch (IllegalAccessException IAe) { } ans += " <annotation name=\"" + AbstractConnection.dollar_pattern.matcher(t.getName()).replaceAll(".") + "\" value=\"" + value + "\" />\n"; } return ans; } @SuppressWarnings("unchecked") private Map<MethodTuple, Method> getExportedMethods(Class c) throws DBusException { if (DBusInterface.class.equals(c)) return new HashMap<MethodTuple, Method>(); Map<MethodTuple, Method> m = new HashMap<MethodTuple, Method>(); for (Class i : c.getInterfaces()) if (DBusInterface.class.equals(i)) { // add this class's public methods if (null != c.getAnnotation(DBusInterfaceName.class)) { String name = ((DBusInterfaceName) c.getAnnotation(DBusInterfaceName.class)).value(); introspectiondata += " <interface name=\"" + name + "\">\n"; DBusSignal.addInterfaceMap(c.getName(), name); } else { // don't let people export things which don't have a // valid D-Bus interface name if (c.getName().equals(c.getSimpleName())) throw new DBusException(getString("interfaceNotAllowedOutsidePackage")); if (c.getName().length() > DBusConnection.MAX_NAME_LENGTH) throw new DBusException(getString("introspectInterfaceExceedCharacters") + c.getName()); else introspectiondata += " <interface name=\"" + AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll(".") + "\">\n"; } introspectiondata += getAnnotations(c); for (Method meth : c.getDeclaredMethods()) if (Modifier.isPublic(meth.getModifiers())) { String ms = ""; String name; if (meth.isAnnotationPresent(DBusMemberName.class)) name = meth.getAnnotation(DBusMemberName.class).value(); else name = meth.getName(); if (name.length() > DBusConnection.MAX_NAME_LENGTH) throw new DBusException(getString("introspectMethodExceedCharacters") + name); introspectiondata += " <method name=\"" + name + "\" >\n"; introspectiondata += getAnnotations(meth); for (Class ex : meth.getExceptionTypes()) if (DBusExecutionException.class.isAssignableFrom(ex)) introspectiondata += " <annotation name=\"org.freedesktop.DBus.Method.Error\" value=\"" + AbstractConnection.dollar_pattern.matcher(ex.getName()).replaceAll(".") + "\" />\n"; for (Type pt : meth.getGenericParameterTypes()) for (String s : Marshalling.getDBusType(pt)) { introspectiondata += " <arg type=\"" + s + "\" direction=\"in\"/>\n"; ms += s; } if (!Void.TYPE.equals(meth.getGenericReturnType())) { if (Tuple.class.isAssignableFrom((Class) meth.getReturnType())) { ParameterizedType tc = (ParameterizedType) meth.getGenericReturnType(); Type[] ts = tc.getActualTypeArguments(); for (Type t : ts) if (t != null) for (String s : Marshalling.getDBusType(t)) introspectiondata += " <arg type=\"" + s + "\" direction=\"out\"/>\n"; } else if (Object[].class.equals(meth.getGenericReturnType())) { throw new DBusException(getString("cannotIntrospectReturnType")); } else for (String s : Marshalling.getDBusType(meth.getGenericReturnType())) introspectiondata += " <arg type=\"" + s + "\" direction=\"out\"/>\n"; } introspectiondata += " </method>\n"; m.put(new MethodTuple(name, ms), meth); } for (Class sig : c.getDeclaredClasses()) if (DBusSignal.class.isAssignableFrom(sig)) { String name; if (sig.isAnnotationPresent(DBusMemberName.class)) { name = ((DBusMemberName) sig.getAnnotation(DBusMemberName.class)).value(); DBusSignal.addSignalMap(sig.getSimpleName(), name); } else name = sig.getSimpleName(); if (name.length() > DBusConnection.MAX_NAME_LENGTH) throw new DBusException(getString("introspectSignalExceedCharacters") + name); introspectiondata += " <signal name=\"" + name + "\">\n"; Constructor con = sig.getConstructors()[0]; Type[] ts = con.getGenericParameterTypes(); for (int j = 1; j < ts.length; j++) for (String s : Marshalling.getDBusType(ts[j])) introspectiondata += " <arg type=\"" + s + "\" direction=\"out\" />\n"; introspectiondata += getAnnotations(sig); introspectiondata += " </signal>\n"; } introspectiondata += " </interface>\n"; } else { // recurse m.putAll(getExportedMethods(i)); } return m; } Map<MethodTuple, Method> methods; Reference<DBusInterface> object; String introspectiondata; public ExportedObject(DBusInterface object, boolean weakreferences) throws DBusException { if (weakreferences) this.object = new WeakReference<DBusInterface>(object); else this.object = new StrongReference<DBusInterface>(object); introspectiondata = ""; methods = getExportedMethods(object.getClass()); introspectiondata += " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" + " <method name=\"Introspect\">\n" + " <arg type=\"s\" direction=\"out\"/>\n" + " </method>\n" + " </interface>\n"; introspectiondata += " <interface name=\"org.freedesktop.DBus.Peer\">\n" + " <method name=\"Ping\">\n" + " </method>\n" + " </interface>\n"; } }