All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.freedesktop.dbus.DBusSignal Maven / Gradle / Ivy

Go to download

Improved version of the DBus-Java library provided by freedesktop.org (https://dbus.freedesktop.org/doc/dbus-java/).

There is a newer version: 3.3.2
Show newest version
/*
   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.Constructor;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.MessageFormatException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DBusSignal extends Message {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    DBusSignal() {
    }

    public DBusSignal(String source, String path, String iface, String member, String sig, Object... args) throws DBusException {
        super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0);

        if (null == path || null == member || null == iface) {
            throw new MessageFormatException(t("Must specify object path, interface and signal name to Signals."));
        }
        headers.put(Message.HeaderField.PATH, path);
        headers.put(Message.HeaderField.MEMBER, member);
        headers.put(Message.HeaderField.INTERFACE, iface);

        Vector hargs = new Vector();
        hargs.add(new Object[] {
                Message.HeaderField.PATH, new Object[] {
                        ArgumentType.OBJECT_PATH_STRING, path
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.INTERFACE, new Object[] {
                        ArgumentType.STRING_STRING, iface
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.MEMBER, new Object[] {
                        ArgumentType.STRING_STRING, member
                }
        });

        if (null != source) {
            headers.put(Message.HeaderField.SENDER, source);
            hargs.add(new Object[] {
                    Message.HeaderField.SENDER, new Object[] {
                            ArgumentType.STRING_STRING, source
                    }
            });
        }

        if (null != sig) {
            hargs.add(new Object[] {
                    Message.HeaderField.SIGNATURE, new Object[] {
                            ArgumentType.SIGNATURE_STRING, sig
                    }
            });
            headers.put(Message.HeaderField.SIGNATURE, sig);
            setArgs(args);
        }

        blen = new byte[4];
        appendBytes(blen);
        append("ua(yv)", ++serial, hargs.toArray());
        pad((byte) 8);

        long counter = bytecounter;
        if (null != sig) {
            append(sig, args);
        }
        marshallint(bytecounter - counter, blen, 0, 4);
        bodydone = true;
    }

    // CHECKSTYLE:OFF
    static class internalsig extends DBusSignal {
        internalsig(String source, String objectpath, String type, String name, String sig, Object[] parameters, long serial) throws DBusException {
            super(source, objectpath, type, name, sig, parameters, serial);
        }
    }
    // CHECKSTYLE:ON

    private static Map, Type[]>                            typeCache  = new HashMap, Type[]>();
    private static Map>                            classCache = new HashMap>();
    private static Map, Constructor> conCache   = new HashMap, Constructor>();
    private static Map                                                 signames   = new HashMap();
    private static Map                                                 intnames   = new HashMap();
    private Class                                                c;
    private boolean                                                                    bodydone   = false;
    private byte[]                                                                     blen;

    static void addInterfaceMap(String java, String dbus) {
        intnames.put(dbus, java);
    }

    static void addSignalMap(String java, String dbus) {
        signames.put(dbus, java);
    }

    static DBusSignal createSignal(Class c, String source, String objectpath, String sig, long serial, Object... parameters) throws DBusException {
        String type = "";
        if (null != c.getEnclosingClass()) {
            if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class)) {
                type = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value();
            } else {
                type = AbstractConnection.DOLLAR_PATTERN.matcher(c.getEnclosingClass().getName()).replaceAll(".");
            }

        } else {
            throw new DBusException(t("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package."));
        }
        DBusSignal s = new internalsig(source, objectpath, type, c.getSimpleName(), sig, parameters, serial);
        s.c = c;
        return s;
    }

    @SuppressWarnings("unchecked")
    private static Class createSignalClass(String intname, String signame) throws DBusException {
        String name = intname + '$' + signame;
        Class c = classCache.get(name);
        if (null == c) {
            c = DBusMatchRule.getCachedSignalType(name);
        }
        if (null != c) {
            return c;
        }
        do {
            try {
                c = (Class) Class.forName(name);
            } catch (ClassNotFoundException exCnf) {
            }
            name = name.replaceAll("\\.([^\\.]*)$", "\\$$1");
        } while (null == c && name.matches(".*\\..*"));
        if (null == c) {
            throw new DBusException(t("Could not create class from signal ") + intname + '.' + signame);
        }
        classCache.put(name, c);
        return c;
    }

    @SuppressWarnings("unchecked")
    DBusSignal createReal(AbstractConnection conn) throws DBusException {
        String intname = intnames.get(getInterface());
        String signame = signames.get(getName());
        if (null == intname) {
            intname = getInterface();
        }
        if (null == signame) {
            signame = getName();
        }
        if (null == c) {
            c = createSignalClass(intname, signame);
        }

        logger.debug("Converting signal to type: " + c);
        Type[] types = typeCache.get(c);
        Constructor con = conCache.get(c);
        if (null == types) {
            con = (Constructor) c.getDeclaredConstructors()[0];
            conCache.put(c, con);
            Type[] ts = con.getGenericParameterTypes();
            types = new Type[ts.length - 1];
            for (int i = 1; i < ts.length; i++) {
                if (ts[i] instanceof TypeVariable) {
                    for (Type b : ((TypeVariable) ts[i]).getBounds()) {
                        types[i - 1] = b;
                    }
                } else {
                    types[i - 1] = ts[i];
                }
            }
            typeCache.put(c, types);
        }

        try {
            DBusSignal s;
            Object[] args = Marshalling.deSerializeParameters(getParameters(), types, conn);
            if (null == args) {
                s = con.newInstance(getPath());
            } else {
                Object[] params = new Object[args.length + 1];
                params[0] = getPath();
                System.arraycopy(args, 0, params, 1, args.length);

                logger.debug("Creating signal of type " + c + " with parameters " + Arrays.deepToString(params));
                s = con.newInstance(params);
            }
            s.headers = headers;
            s.wiredata = wiredata;
            s.bytecounter = wiredata.length;
            return s;
        } catch (Exception e) {
            if (AbstractConnection.EXCEPTION_DEBUG) {
                logger.error("", e);
            }
            throw new DBusException(e.getMessage());
        }
    }

    /**
    * Create a new signal.
    * This contructor MUST be called by all sub classes.
    * @param objectpath The path to the object this is emitted from.
    * @param args The parameters of the signal.
    * @throws DBusException This is thrown if the subclass is incorrectly defined.
    */
    @SuppressWarnings("unchecked")
    protected DBusSignal(String objectpath, Object... args) throws DBusException {
        super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0);

        if (!objectpath.matches(AbstractConnection.OBJECT_REGEX)) {
            throw new DBusException(t("Invalid object path: ") + objectpath);
        }

        Class tc = getClass();
        String member;
        if (tc.isAnnotationPresent(DBusMemberName.class)) {
            member = tc.getAnnotation(DBusMemberName.class).value();
        } else {
            member = tc.getSimpleName();
        }
        String iface = null;
        Class enc = tc.getEnclosingClass();
        if (null == enc || !DBusInterface.class.isAssignableFrom(enc) || enc.getName().equals(enc.getSimpleName())) {
            throw new DBusException(t("Signals must be declared as a member of a class implementing DBusInterface which is the member of a package."));
        } else if (null != enc.getAnnotation(DBusInterfaceName.class)) {
            iface = enc.getAnnotation(DBusInterfaceName.class).value();
        } else {
            iface = AbstractConnection.DOLLAR_PATTERN.matcher(enc.getName()).replaceAll(".");
        }

        headers.put(Message.HeaderField.PATH, objectpath);
        headers.put(Message.HeaderField.MEMBER, member);
        headers.put(Message.HeaderField.INTERFACE, iface);

        Vector hargs = new Vector();
        hargs.add(new Object[] {
                Message.HeaderField.PATH, new Object[] {
                        ArgumentType.OBJECT_PATH_STRING, objectpath
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.INTERFACE, new Object[] {
                        ArgumentType.STRING_STRING, iface
                }
        });
        hargs.add(new Object[] {
                Message.HeaderField.MEMBER, new Object[] {
                        ArgumentType.STRING_STRING, member
                }
        });

        String sig = null;
        if (0 < args.length) {
            try {
                Type[] types = typeCache.get(tc);
                if (null == types) {
                    Constructor con = (Constructor) tc.getDeclaredConstructors()[0];
                    conCache.put(tc, con);
                    Type[] ts = con.getGenericParameterTypes();
                    types = new Type[ts.length - 1];
                    for (int i = 1; i <= types.length; i++) {
                        if (ts[i] instanceof TypeVariable) {
                            types[i - 1] = ((TypeVariable) ts[i]).getBounds()[0];
                        } else {
                            types[i - 1] = ts[i];
                        }
                    }
                    typeCache.put(tc, types);
                }
                sig = Marshalling.getDBusType(types);
                hargs.add(new Object[] {
                        Message.HeaderField.SIGNATURE, new Object[] {
                                ArgumentType.SIGNATURE_STRING, sig
                        }
                });
                headers.put(Message.HeaderField.SIGNATURE, sig);
                setArgs(args);
            } catch (Exception e) {
                if (AbstractConnection.EXCEPTION_DEBUG) {
                    logger.error("", e);
                }
                throw new DBusException(t("Failed to add signal parameters: ") + e.getMessage());
            }
        }

        blen = new byte[4];
        appendBytes(blen);
        append("ua(yv)", ++serial, hargs.toArray());
        pad((byte) 8);
    }

    void appendbody(AbstractConnection conn) throws DBusException {
        if (bodydone) {
            return;
        }

        Type[] types = typeCache.get(getClass());
        Object[] args = Marshalling.convertParameters(getParameters(), types, conn);
        setArgs(args);
        String sig = getSig();

        long counter = bytecounter;
        if (null != args && 0 < args.length) {
            append(sig, args);
        }
        marshallint(bytecounter - counter, blen, 0, 4);
        bodydone = true;
    }
}