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

org.freedesktop.dbus.bin.CreateInterface 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.bin;

import static org.freedesktop.dbus.bin.IdentifierMangler.mangle;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.freedesktop.dbus.Marshalling;
import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.connections.impl.DBusConnection.DBusBusType;
import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.exceptions.DBusExecutionException;
import org.freedesktop.dbus.interfaces.Introspectable;
import org.freedesktop.dbus.types.DBusStructType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Converts a DBus XML file into Java interface definitions.
 */
public class CreateInterface {
    @SuppressWarnings("unchecked")
    private static String collapseType(Type t, Set imports, Map structs, boolean container, boolean fullnames) throws DBusException {
        if (t instanceof ParameterizedType) {
            String s;
            Class c = (Class) ((ParameterizedType) t).getRawType();
            if (null != structs && t instanceof DBusStructType) {
                int num = 1;
                String name = "Struct";
                while (null != structs.get(new StructStruct(name + num))) {
                    num++;
                }
                name = name + num;
                structs.put(new StructStruct(name), ((ParameterizedType) t).getActualTypeArguments());
                return name;
            }
            if (null != imports) {
                imports.add(c.getName());
            }
            if (fullnames) {
                return c.getName();
            } else {
                s = c.getSimpleName();
            }
            s += '<';
            Type[] ts = ((ParameterizedType) t).getActualTypeArguments();
            for (Type st : ts) {
                s += collapseType(st, imports, structs, true, fullnames) + ',';
            }
            s = s.replaceAll(",$", ">");
            return s;
        } else if (t instanceof Class) {
            Class c = (Class) t;
            if (c.isArray()) {
                return collapseType(c.getComponentType(), imports, structs, container, fullnames) + "[]";
            } else {
                Package p = c.getPackage();
                if (null != imports && !"java.lang".equals(p.getName())) {
                    imports.add(c.getName());
                }
                if (container) {
                    if (fullnames) {
                        return c.getName();
                    } else {
                        return c.getSimpleName();
                    }
                } else {
                    try {
                        Field f = c.getField("TYPE");
                        Class d = (Class) f.get(c);
                        return d.getSimpleName();
                    } catch (Exception e) {
                        return c.getSimpleName();
                    }
                }
            }
        } else {
            return "";
        }
    }

    private static String getJavaType(String dbus, Set imports, Map structs, boolean container, boolean fullnames) throws DBusException {
        if (null == dbus || "".equals(dbus)) {
            return "";
        }
        List v = new ArrayList<>();
        Marshalling.getJavaType(dbus, v, 1);
        Type t = v.get(0);
        return collapseType(t, imports, structs, container, fullnames);
    }

    // CHECKSTYLE:OFF
    public String comment = "";
    boolean       builtin;
    // CHECKSTYLE:ON

    public CreateInterface(PrintStreamFactory _factory, boolean _builtin) {
        this.factory = _factory;
        this.builtin = _builtin;
    }

    String parseReturns(List out, Set imports, Map tuples, Map structs) throws DBusException {
        String[] names = new String[] {
                "Pair", "Triplet", "Quad", "Quintuple", "Sextuple", "Septuple"
        };
        String sig = "";
        String name = null;
        switch (out.size()) {
        case 0:
            sig += "void ";
            break;
        case 1:
            sig += getJavaType(out.get(0).getAttribute("type"), imports, structs, false, false) + " ";
            break;
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
            name = names[out.size() - 2];
        default:
            if (null == name) {
                name = "NTuple" + out.size();
            }

            tuples.put(name, out.size());
            sig += name + "<";
            for (Element arg : out) {
                sig += getJavaType(arg.getAttribute("type"), imports, structs, true, false) + ", ";
            }
            sig = sig.replaceAll(", $", "> ");
            break;
        }
        return sig;
    }

    String parseMethod(Element meth, Set imports, Map tuples, Map structs, Set exceptions, Set anns) throws DBusException {
        List in = new ArrayList<>();
        List out = new ArrayList<>();
        if (null == meth.getAttribute("name") || "".equals(meth.getAttribute("name"))) {
            System.err.println("ERROR: Method name was blank, failed");
            System.exit(1);
        }
        String annotations = "";
        String throwses = null;

        for (Node a : new IterableNodeList(meth.getChildNodes())) {

            if (Node.ELEMENT_NODE != a.getNodeType()) {
                continue;
            }

            checkNode(a, "arg", "annotation");

            if ("arg".equals(a.getNodeName())) {
                Element arg = (Element) a;

                // methods default to in
                if ("out".equals(arg.getAttribute("direction"))) {
                    out.add(arg);
                } else {
                    in.add(arg);
                }
            } else if ("annotation".equals(a.getNodeName())) {
                Element e = (Element) a;
                if (e.getAttribute("name").equals("org.freedesktop.DBus.Method.Error")) {
                    if (null == throwses) {
                        throwses = e.getAttribute("value");
                    } else {
                        throwses += ", " + e.getAttribute("value");
                    }
                    exceptions.add(e.getAttribute("value"));
                } else {
                    annotations += parseAnnotation(e, imports, anns);
                }
            }
        }

        String sig = "";
        comment = "";
        sig += parseReturns(out, imports, tuples, structs);

        sig += mangle(meth.getAttribute("name")) + "(";

        char defaultname = 'a';
        String params = "";
        for (Element arg : in) {
            String type = getJavaType(arg.getAttribute("type"), imports, structs, false, false);
            String name = arg.getAttribute("name");
            if (null == name || "".equals(name)) {
                name = "" + (defaultname++);
            }
            params += type + " " + mangle(name) + ", ";
        }
        return ("".equals(comment) ? "" : "   /**\n" + comment + "   */\n") + annotations + "  public " + sig + params.replaceAll("..$", "") + ")" + (null == throwses ? "" : " throws " + throwses) + ";";
    }

    String parseSignal(Element signal, Set imports, Map structs, Set anns) throws DBusException {
        Map params = new HashMap();
        List porder = new ArrayList<>();
        char defaultname = 'a';
        imports.add("org.freedesktop.dbus.DBusSignal");
        imports.add("org.freedesktop.dbus.exceptions.DBusException");
        String annotations = "";
        for (Node a : new IterableNodeList(signal.getChildNodes())) {

            if (Node.ELEMENT_NODE != a.getNodeType()) {
                continue;
            }

            checkNode(a, "arg", "annotation");

            if ("annotation".equals(a.getNodeName())) {
                annotations += parseAnnotation((Element) a, imports, anns);
            } else {
                Element arg = (Element) a;
                String type = getJavaType(arg.getAttribute("type"), imports, structs, false, false);
                String name = arg.getAttribute("name");
                if (null == name || "".equals(name)) {
                    name = "" + (defaultname++);
                }
                params.put(mangle(name), type);
                porder.add(mangle(name));
            }
        }

        String out = "";
        out += annotations;
        out += "   public static class " + signal.getAttribute("name");
        out += " extends DBusSignal\n   {\n";
        for (String name : porder) {
            out += "      public final " + params.get(name) + " " + name + ";\n";
        }
        out += "      public " + signal.getAttribute("name") + "(String path";
        for (String name : porder) {
            out += ", " + params.get(name) + " " + name;
        }
        out += ") throws DBusException\n      {\n         super(path";
        for (String name : porder) {
            out += ", " + name;
        }
        out += ");\n";
        for (String name : porder) {
            out += "         this." + name + " = " + name + ";\n";
        }
        out += "      }\n";

        out += "   }\n";
        return out;
    }

    String parseAnnotation(Element ann, Set imports, Set annotations) {
        String s = "  @" + ann.getAttribute("name").replaceAll(".*\\.([^.]*)$", "$1") + "(";
        if (null != ann.getAttribute("value") && !"".equals(ann.getAttribute("value"))) {
            s += '"' + ann.getAttribute("value") + '"';
        }
        imports.add(ann.getAttribute("name"));
        annotations.add(ann.getAttribute("name"));
        return s += ")\n";
    }

    void parseInterface(Element iface, PrintStream out, Map tuples, Map structs, Set exceptions, Set anns) throws DBusException {
        if (null == iface.getAttribute("name") || "".equals(iface.getAttribute("name"))) {
            System.err.println("ERROR: Interface name was blank, failed");
            System.exit(1);
        }

        out.println("package " + iface.getAttribute("name").replaceAll("\\.[^.]*$", "") + ";");

        String methods = "";
        String signals = "";
        String annotations = "";
        Set imports = new TreeSet();
        imports.add("org.freedesktop.dbus.DBusInterface");
        for (Node meth : new IterableNodeList(iface.getChildNodes())) {

            if (Node.ELEMENT_NODE != meth.getNodeType()) {
                continue;
            }

            checkNode(meth, "method", "signal", "property", "annotation");

            if ("method".equals(meth.getNodeName())) {
                methods += parseMethod((Element) meth, imports, tuples, structs, exceptions, anns) + "\n";
            } else if ("signal".equals(meth.getNodeName())) {
                signals += parseSignal((Element) meth, imports, structs, anns);
            } else if ("property".equals(meth.getNodeName())) {
                System.err.println("WARNING: Ignoring property");
            } else if ("annotation".equals(meth.getNodeName())) {
                annotations += parseAnnotation((Element) meth, imports, anns);
            }
        }

        if (imports.size() > 0) {
            for (String i : imports) {
                out.println("import " + i + ";");
            }
        }

        out.print(annotations);
        out.print("public interface " + iface.getAttribute("name").replaceAll("^.*\\.([^.]*)$", "$1"));
        out.println(" extends DBusInterface");
        out.println("{");
        out.println(signals);
        out.println(methods);
        out.println("}");
    }

    void createException(String name, String pack, PrintStream out) throws DBusException {
        out.println("package " + pack + ";");
        out.println("import org.freedesktop.dbus.DBusExecutionException;");
        out.print("public class " + name);
        out.println(" extends DBusExecutionException");
        out.println("{");
        out.println("   public " + name + "(String message)");
        out.println("   {");
        out.println("      super(message);");
        out.println("   }");
        out.println("}");
    }

    void createAnnotation(String name, String pack, PrintStream out) throws DBusException {
        out.println("package " + pack + ";");
        out.println("import java.lang.annotation.Retention;");
        out.println("import java.lang.annotation.RetentionPolicy;");
        out.println("@Retention(RetentionPolicy.RUNTIME)");
        out.println("public @interface " + name);
        out.println("{");
        out.println("   String value();");
        out.println("}");
    }

    void createStruct(String name, Type[] type, String pack, PrintStream out, Map existing) throws DBusException, IOException {
        out.println("package " + pack + ";");

        Set imports = new TreeSet();
        imports.add("org.freedesktop.dbus.Position");
        imports.add("org.freedesktop.dbus.Struct");
        Map structs = new HashMap(existing);
        String[] types = new String[type.length];
        for (int i = 0; i < type.length; i++) {
            types[i] = collapseType(type[i], imports, structs, false, false);
        }

        for (String im : imports) {
            out.println("import " + im + ";");
        }

        out.println("public final class " + name + " extends Struct");
        out.println("{");
        int i = 0;
        char c = 'a';
        String params = "";
        for (String t : types) {
            out.println("   @Position(" + i++ + ")");
            out.println("   public final " + t + " " + c + ";");
            params += t + " " + c + ", ";
            c++;
        }
        out.println("  public " + name + "(" + params.replaceAll("..$", "") + ")");
        out.println("  {");
        for (char d = 'a'; d < c; d++) {
            out.println("   this." + d + " = " + d + ";");
        }

        out.println("  }");
        out.println("}");

        structs = StructStruct.fillPackages(structs, pack);
        Map tocreate = new HashMap(structs);
        for (StructStruct ss : existing.keySet()) {
            tocreate.remove(ss);
        }
        createStructs(tocreate, structs);
    }

    void createTuple(String name, int num, String pack, PrintStream out) throws DBusException {
        out.println("package " + pack + ";");
        out.println("import org.freedesktop.dbus.Position;");
        out.println("import org.freedesktop.dbus.Tuple;");
        out.println("/** Just a typed container class */");
        out.print("public final class " + name);
        String types = " <";
        for (char v = 'A'; v < 'A' + num; v++) {
            types += v + ",";
        }
        out.print(types.replaceAll(",$", "> "));
        out.println("extends Tuple");
        out.println("{");

        char t = 'A';
        char n = 'a';
        for (int i = 0; i < num; i++, t++, n++) {
            out.println("   @Position(" + i + ")");
            out.println("   public final " + t + " " + n + ";");
        }

        out.print("   public " + name + "(");
        String sig = "";
        t = 'A';
        n = 'a';
        for (int i = 0; i < num; i++, t++, n++) {
            sig += t + " " + n + ", ";
        }
        out.println(sig.replaceAll(", $", ")"));
        out.println("   {");
        for (char v = 'a'; v < 'a' + num; v++) {
            out.println("      this." + v + " = " + v + ";");
        }
        out.println("   }");

        out.println("}");
    }

    void parseRoot(Element root) throws DBusException, IOException {
        Map structs = new HashMap();
        Set exceptions = new TreeSet();
        Set annotations = new TreeSet();

        for (Node iface : new IterableNodeList(root.getChildNodes())) {

            if (Node.ELEMENT_NODE != iface.getNodeType()) {
                continue;
            }

            checkNode(iface, "interface", "node");

            if ("interface".equals(iface.getNodeName())) {

                Map tuples = new HashMap();
                String name = ((Element) iface).getAttribute("name");
                String file = name.replaceAll("\\.", "/") + ".java";
                String path = file.replaceAll("/[^/]*$", "");
                String pack = name.replaceAll("\\.[^.]*$", "");

                // don't create interfaces in org.freedesktop.DBus by default
                if (pack.startsWith("org.freedesktop.DBus") && !builtin) {
                    continue;
                }

                factory.init(file, path);
                parseInterface((Element) iface, factory.createPrintStream(file), tuples, structs, exceptions, annotations);

                structs = StructStruct.fillPackages(structs, pack);
                createTuples(tuples, pack);
            } else if ("node".equals(iface.getNodeName())) {
                parseRoot((Element) iface);
            } else {
                System.err.println("ERROR: Unknown node: " + iface.getNodeName());
                System.exit(1);
            }
        }

        createStructs(structs, structs);
        createExceptions(exceptions);
        createAnnotations(annotations);
    }

    private void createAnnotations(Set annotations) throws DBusException, IOException {
        for (String fqn : annotations) {
            String name = fqn.replaceAll("^.*\\.([^.]*)$", "$1");
            String pack = fqn.replaceAll("\\.[^.]*$", "");
            // don't create things in org.freedesktop.DBus by default
            if (pack.startsWith("org.freedesktop.DBus") && !builtin) {
                continue;
            }
            String path = pack.replaceAll("\\.", "/");
            String file = name.replaceAll("\\.", "/") + ".java";
            factory.init(file, path);
            createAnnotation(name, pack, factory.createPrintStream(path, name));
        }
    }

    private void createExceptions(Set exceptions) throws DBusException, IOException {
        for (String fqn : exceptions) {
            String name = fqn.replaceAll("^.*\\.([^.]*)$", "$1");
            String pack = fqn.replaceAll("\\.[^.]*$", "");
            // don't create things in org.freedesktop.DBus by default
            if (pack.startsWith("org.freedesktop.DBus") && !builtin) {
                continue;
            }
            String path = pack.replaceAll("\\.", "/");
            String file = name.replaceAll("\\.", "/") + ".java";
            factory.init(file, path);
            createException(name, pack, factory.createPrintStream(path, name));
        }
    }

    private void createStructs(Map structs, Map existing) throws DBusException, IOException {
        for (StructStruct ss : structs.keySet()) {
            String file = ss.name.replaceAll("\\.", "/") + ".java";
            String path = ss.pack.replaceAll("\\.", "/");
            factory.init(file, path);
            createStruct(ss.name, structs.get(ss), ss.pack, factory.createPrintStream(path, ss.name), existing);
        }
    }

    private void createTuples(Map typeMap, String pack) throws DBusException, IOException {
        for (String tname : typeMap.keySet()) {
            createTuple(tname, typeMap.get(tname), pack, factory.createPrintStream(pack.replaceAll("\\.", "/"), tname));
        }
    }

    public abstract static class PrintStreamFactory {

        public abstract void init(String file, String path);

        /**
         * @param path path
         * @param tname target name
         * @return PrintStream
         * @throws IOException may throw exception on failure
         */
        public PrintStream createPrintStream(String path, String tname) throws IOException {
            final String file = path + "/" + tname + ".java";
            return createPrintStream(file);
        }

        /**
         * @param file file to print to
         * @return PrintStream
         * @throws IOException may throw exception on failure
         */
        public abstract PrintStream createPrintStream(final String file) throws IOException;

    }

    static class ConsoleStreamFactory extends PrintStreamFactory {

        @Override
        public void init(String file, String path) {
        }

        @Override
        public PrintStream createPrintStream(String file) throws IOException {
            System.out.println("/* File: " + file + " */");
            return System.out;
        }

        @Override
        public PrintStream createPrintStream(String path, String tname) throws IOException {
            return super.createPrintStream(path, tname);
        }

    }

    static class FileStreamFactory extends PrintStreamFactory {
        @Override
        public void init(String file, String path) {
            new File(path).mkdirs();
        }

        /**
         * @param file
         * @return
         * @throws IOException
         */
        @Override
        public PrintStream createPrintStream(final String file) throws IOException {
            return new PrintStream(new FileOutputStream(file));
        }

    }

    static void checkNode(Node n, String... names) {
        String expected = "";
        for (String name : names) {
            if (name.equals(n.getNodeName())) {
                return;
            }
            expected += name + " or ";
        }
        System.err.println(String.format("ERROR: Expected %s, got %s, failed.", expected.replaceAll("....$", ""), n.getNodeName()));
        System.exit(1);
    }

    private final PrintStreamFactory factory;

    static class Config {
        // CHECKSTYLE:OFF
        DBusBusType     bus       = DBusBusType.SESSION;
        String  busname   = null;
        String  object    = null;
        File    datafile  = null;
        boolean printtree = false;
        boolean fileout   = false;
        boolean builtin   = false;
        boolean ignoreDtd = true;
        // CHECKSTYLE:ON
    }

    static void printSyntax() {
        printSyntax(System.err);
    }

    static void printSyntax(PrintStream o) {
        o.println("Syntax: CreateInterface  [file | busname object]");
        o.println("        Options: --no-ignore-builtin  --enable-dtd-validation --system -y --session -s --create-files -f --help -h --version -v");
    }

    public static void version() {
        System.out.println("Java D-Bus Version " + System.getProperty("Version"));
        System.exit(1);
    }

    static Config parseParams(String[] args) {
        Config config = new Config();
        for (String p : args) {
            if ("--system".equals(p) || "-y".equals(p)) {
                config.bus = DBusBusType.SYSTEM;
            } else if ("--session".equals(p) || "-s".equals(p)) {
                config.bus = DBusBusType.SESSION;
            } else if ("--no-ignore-builtin".equals(p)) {
                config.builtin = true;
            } else if ("--create-files".equals(p) || "-f".equals(p)) {
                config.fileout = true;
            } else if ("--print-tree".equals(p) || "-p".equals(p)) {
                config.printtree = true;
            } else if ("--enable-dtd-validation".equals(p)) {
                config.ignoreDtd = false;
            } else if ("--help".equals(p) || "-h".equals(p)) {
                printSyntax(System.out);
                System.exit(0);
            } else if ("--version".equals(p) || "-v".equals(p)) {
                version();
                System.exit(0);
            } else if (p.startsWith("-")) {
                System.err.println("ERROR: Unknown option: " + p);
                printSyntax();
                System.exit(1);
            } else {
                if (null == config.busname) {
                    config.busname = p;
                } else if (null == config.object) {
                    config.object = p;
                } else {
                    printSyntax();
                    System.exit(1);
                }
            }
        }
        if (null == config.busname) {
            printSyntax();
            System.exit(1);
        } else if (null == config.object) {
            config.datafile = new File(config.busname);
            config.busname = null;
        }
        return config;
    }

    public static void main(String[] args) throws Exception {
        Config config = parseParams(args);

        Reader introspectdata = null;

        if (null != config.busname) {
            try {
                DBusConnection conn = DBusConnection.getConnection(config.bus);
                Introspectable in = conn.getRemoteObject(config.busname, config.object, Introspectable.class);
                String id = in.Introspect();
                if (null == id) {
                    System.err.println("ERROR: Failed to get introspection data");
                    System.exit(1);
                }
                introspectdata = new StringReader(id);
                conn.disconnect();
            } catch (DBusException exD) {
                System.err.println("ERROR: Failure in DBus Communications: " + exD.getMessage());
                System.exit(1);
            } catch (DBusExecutionException exDee) {
                System.err.println("ERROR: Failure in DBus Communications: " + exDee.getMessage());
                System.exit(1);

            }
        } else if (null != config.datafile) {
            try {
                introspectdata = new InputStreamReader(new FileInputStream(config.datafile));
            } catch (FileNotFoundException exFnf) {
                System.err.println("ERROR: Could not find introspection file: " + exFnf.getMessage());
                System.exit(1);
            }
        }
        try {
            PrintStreamFactory factory = config.fileout ? new FileStreamFactory() : new ConsoleStreamFactory();
            CreateInterface createInterface = new CreateInterface(factory, config.builtin);
            createInterface.createInterface(introspectdata, config);
        } catch (DBusException exD) {
            System.err.println("ERROR: " + exD.getMessage());
            System.exit(1);
        }
    }

    /** Output the interface for the supplied xml reader
    * @param _introspectdata The introspect data reader
    * @param _config config
    * @throws ParserConfigurationException If the xml parser could not be configured
    * @throws SAXException If a problem occurs reading the xml data
    * @throws IOException If an IO error occurs
    * @throws DBusException If the dbus related error occurs
    */
    public void createInterface(Reader _introspectdata, Config _config) throws ParserConfigurationException, SAXException, IOException, DBusException {
        DocumentBuilderFactory lfactory = DocumentBuilderFactory.newInstance();

        if (_config != null && _config.ignoreDtd) { // if dtd validation is disabled (default)
            lfactory.setValidating(false);
            lfactory.setNamespaceAware(true);
            lfactory.setFeature("http://xml.org/sax/features/namespaces", false);
            lfactory.setFeature("http://xml.org/sax/features/validation", false);
            lfactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
            lfactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        }

        DocumentBuilder builder = lfactory.newDocumentBuilder();

        Document document = builder.parse(new InputSource(_introspectdata));

        Element root = document.getDocumentElement();
        checkNode(root, "node");
        parseRoot(root);

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy