view dbus-d/dsrc/org/freedesktop/dbus/tool/CreateInterface.d @ 6:963d271f7c25

disabled event stuff, to have working example
author Frank Benoit <benoit@tionex.de>
date Sun, 28 Oct 2007 19:35:38 +0100
parents 7c2c75740370
children 198c379caaa7
line wrap: on
line source

/*
 * Copyright (C) 2007  Frank Benoit
 *
 * Licensed under the Academic Free License version 2.1
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
module org.freedesktop.dbus.tool.CreateInterface;

import tango.io.Stdout;
import tango.io.Print;
import tango.text.convert.Integer : toUtf8;
import tango.text.Util : layout, split, join, replace;
import tango.net.Uri; // make dsss and linker happy :(
import tango.core.Exception : TracedException;

import org.freedesktop.dbus.tool.XmlSupport;

class TerminateException : TracedException {
    int code;
    this( int code ){
        super( null );
        this.code = code;
    }
}
const char[] ID_NODE = "node";
const char[] ID_INTF = "interface";
const char[] ID_METH = "method";
const char[] ID_SIGN = "signal";
const char[] ID_PROP = "property";
const char[] ID_ANNO = "annotation";
const char[] ID_ARGU = "arg";
const char[] ID_DIRECTION     = "direction";
const char[] ID_DIRECTION_IN  = "in";
const char[] ID_DIRECTION_OUT = "out";
const char[] ID_TYPE          = "type";
const char[] ANNO_RETURNS     = "org.dsource.dbus.d.Returns";
IntfTree intfTree;

//-----------------------------------------

private void createStdInterfaces() {
    DefInterface intf;
    DefMethod meth;
    DefArgument argu;

    intfTree.add( intf = new DefInterface( "org.freedesktop.DBus.Peer" ));
    {
        intf.addMethod( meth = new DefMethod( "Ping" ));
        meth.complete();

        intf.addMethod( meth = new DefMethod( "GetMachineId" ));
        meth.addArgument( argu = new DefArgument( "machine_uuid", "s", DefArgument.EDir.DIR_OUT));
        meth.addAnnotation( ANNO_RETURNS, "machine_uuid" );
        meth.complete();
    }

    intfTree.add( intf = new DefInterface( "org.freedesktop.DBus.Introspectable" ));
    {
        intf.addMethod( meth = new DefMethod( "Introspect" ));
        meth.addArgument( argu = new DefArgument( "xml_data", "s", DefArgument.EDir.DIR_OUT));
        meth.addAnnotation( ANNO_RETURNS, "xml_data" );
        meth.complete();
    }

    intfTree.add( intf = new DefInterface( "org.freedesktop.DBus.Properties" ));
    {
        intf.addMethod( meth = new DefMethod( "Get" ));
        meth.addArgument( argu = new DefArgument( "intf_name", "s", DefArgument.EDir.DIR_IN ));
        meth.addArgument( argu = new DefArgument( "prop_name", "s", DefArgument.EDir.DIR_IN ));
        meth.addArgument( argu = new DefArgument( "prop"     , "v", DefArgument.EDir.DIR_OUT));
        meth.addAnnotation( ANNO_RETURNS, "prop" );
        meth.complete();

        intf.addMethod( meth = new DefMethod( "Set" ));
        meth.addArgument( argu = new DefArgument( "intf_name", "s", DefArgument.EDir.DIR_IN ));
        meth.addArgument( argu = new DefArgument( "prop_name", "s", DefArgument.EDir.DIR_IN ));
        meth.addArgument( argu = new DefArgument( "prop"     , "v", DefArgument.EDir.DIR_IN ));
        meth.complete();

        intf.addMethod( meth = new DefMethod( "GetAll" ));
        meth.addArgument( argu = new DefArgument( "intf_name", "s"    , DefArgument.EDir.DIR_IN ));
        meth.addArgument( argu = new DefArgument( "allprops" , "a{sv}", DefArgument.EDir.DIR_OUT));
        meth.addAnnotation( ANNO_RETURNS, "allprops" );
        meth.complete();
    }
}


alias char[] DBusObjPath;
alias void function() DBusHandler;

struct DBusVariant{
    byte type;
    union{
        byte v_byte;
        bool v_bool;
    }
}



class IntfTree{
    class Node{
        char[]       mName;
        DefInterface mValue;
        Node[]       mChilds;
    }

    Node mRoot;
    char[][] allInterfaces;

    public this(){
        mRoot = new Node();
    }

    public void add( DefInterface intf ){
        allInterfaces ~= intf.mName;
        char[][] parts = split( intf.mName, "." );

        Node findNode( char[][] names ){
            if( names.length == 0 ){
                return mRoot;
            }
            Node par = findNode( names[ 0 .. $-1 ] );
            foreach( child; par.mChilds ){
                if( child.mName == names[ $-1 ] ){
                    return child;
                }
            }
            auto child = new Node;
            child.mName = names[ $-1 ].dup;
            par.mChilds ~= child;
            return child;
        }

        Node node = findNode( parts );
        node.mValue = intf;
    }

    public DefInterface getInterface( char[] intfName ){
        char[][] parts = split( intfName, "." );

        Node findNode( char[][] names ){
            if( names.length == 0 ){
                return mRoot;
            }
            Node par = findNode( names[ 0 .. $-1 ] );
            foreach( child; par.mChilds ){
                if( child.mName == names[ $-1 ] ){
                    return child;
                }
            }
            return null;
        }
        Node node = findNode( parts );
        if( node is null ){
            return null;
        }
        return node.mValue;
    }

    public char[][] getChildNames( char[] parentName ){
        char[][] parts = split( parentName, "." );

        Node findNode( char[][] names ){
            if( names.length == 0 ){
                return mRoot;
            }
            Node par = findNode( names[ 0 .. $-1 ] );
            foreach( child; par.mChilds ){
                if( child.mName == names[ $-1 ] ){
                    return child;
                }
            }
            return null;
        }
        Node node = findNode( parts );
        if( node is null ){
            return null;
        }
        char[][] res;
        foreach( idx, child; node.mChilds ){
            char[] txt;
            if( parentName.length > 0 ){
                res ~= parentName ~ '.' ~ child.mName;
            }
            else{
                res ~= child.mName;
            }
        }
        return res;
    }
}

class DefInterface{
    char[]      mName;

    DefMethod[]   mMethods;
    DefSignal[]   mSignals;
    DefProperty[] mProperties;
    char[][ char[] ] mAnnotations;

    public this( char[] aName ){
        mName = aName.dup;
    }

    public void addMethod( DefMethod meth ){
        mMethods ~= meth;
    }

    public void addSignal( DefSignal sign ){
        mSignals ~= sign;
    }

    public void addProperty( DefProperty prop ){
        mProperties ~= prop;
    }

    public void addAnnotation( char[] name, char[] value ){
        mAnnotations[ name.dup ] = value.dup;
    }
}

class DefSignal : ArgumentContainer {

    char[]         mName;
    DefArgument[]  mArguments;
    char[][ char[] ] mAnnotations;
    public this( char[] aName ){
        mName = aName.dup;
    }

    public void addArgument( DefArgument aArg ){
        mArguments ~= aArg;
    }
    public void addAnnotation( char[] name, char[] value ){
        mAnnotations[ name.dup ] = value.dup;
    }
}

class DefProperty {

    char[]         mName;
    public this( char[] aName ){
        mName = aName.dup;
    }

}

class DefMethod : ArgumentContainer {
    char[]         mName;
    DefArgument[]  mArguments;
    char[][ char[] ] mAnnotations;

    DefArgument    mDRetArgument;
    DefArgument[]  mDArguments;

    public this( char[] aName ){
        mName = aName.dup;
    }
    public void addArgument( DefArgument aArg ){
        mArguments ~= aArg;
    }
    public void addAnnotation( char[] name, char[] value ){
        mAnnotations[ name.dup ] = value.dup;
    }

    public void complete(){
        char[]* retArgName = ANNO_RETURNS in mAnnotations;
        if( retArgName !is null ){
            int idx = 0;
            bool found = false;
            foreach( arg; mArguments ){
                if( *retArgName == arg.mName && arg.mDirection == DefArgument.EDir.DIR_OUT ){
                    if( found ){
                        error( "there are two or more out arguements in the same method" );
                    }
                    found = true;
                    mDRetArgument = arg;
                }
                else{
                    mDArguments ~= arg;
                }
            }
        }
        else{
            mDRetArgument = null;
            mDArguments = mArguments;
        }
    }


}

interface ArgumentContainer{
    public void addArgument( DefArgument aPar );
}

class DefArgument{

    enum EDir{
        DIR_IN, DIR_OUT
    }

    EDir    mDirection;
    char[]  mName;
    char[]  mType;

    public this( char[] aName, char[] aType, EDir aDir ){
        mName = aName.dup;
        mType = aType.dup;
        mDirection = aDir;
    }

    char[] toDType(){
        char[] rem;
        char[] res = signatureToDType( mType, rem );
        if( rem.length > 0 ){
            error( "not a complete type %0 %1", mType, rem );
        }
        return res;
    }

    private char[] signatureToDType( char[] sigText, out char[] remainingSigText ){

        remainingSigText = null;
        if( sigText.length == 0 ){
            return null;
        }

        remainingSigText = sigText[ 1.. $ ];

        switch( sigText[0] ){
        case 'y': return "byte";
        case 'b': return "bool";
        case 'n': return "short";
        case 'q': return "ushort";
        case 'i': return "int";
        case 'u': return "uint";
        case 'x': return "long";
        case 't': return "ulong";
        case 'd': return "double";
        case 's': return "char[]";
        case 'o': return "DBusObject";
        case 'g': return "DBusSignature";
        case 'v': return "DBusVariant";
        default:
            break;
        }
        if( sigText[0] == 'a' ){
            if( sigText.length > 1 && sigText[ 1 ] == '{' ){
                char[] rem;
                char[] tkey = signatureToDType( sigText[2..$], rem );
                char[] rem2;
                char[] tval = signatureToDType( rem, rem2 );
                char[] res = tval ~ "[ "~ tkey ~" ]";
                if( rem2.length == 0 || rem2[0] != '}' ){
                    error( "no valid closing bracket for dict Entry" );
                }
                remainingSigText = rem2[ 1 .. $ ];
                return res;
            }
            else{
                return signatureToDType( sigText[1..$], remainingSigText ) ~ "[]";
            }
        }
        else if( sigText[0] == '(' ){
            char[][] flds;
            char[] rem = sigText[ 1 .. $ ];
            do{
                char[] rem2;
                flds ~= signatureToDType( rem, rem2 );
                rem = rem2;
            }
            while( rem.length > 0 && rem[ 0 ] != ')' );
            remainingSigText = ( rem.length > 0  ) ? rem[ 1 .. $ ] : null;
            return "Struct!( " ~ join( flds, ", " ) ~ " )";
        }
        error( "unknown type "~sigText );
        assert(false);
    }


}





// array Attt_ AttAtt_i_     alles bis zum abschließenden _
// map   Mtt       zwei typen nach M

// BYTE    121        (ASCII 'y') 8-bit unsigned integer
// BOOLEAN  98        (ASCII 'b')  Boolean value, 0 is FALSE and 1 is TRUE. Everything else is invalid.
// INT16   110        (ASCII 'n') 16-bit signed integer
// UINT16  113        (ASCII 'q') 16-bit unsigned integer
// INT32   105        (ASCII 'i') 32-bit signed integer
// UINT32  117        (ASCII 'u') 32-bit unsigned integer
// INT64   120        (ASCII 'x') 64-bit signed integer
// UINT64  116        (ASCII 't') 64-bit unsigned integer
// DOUBLE  100        (ASCII 'd') IEEE 754 double
// STRING  115        (ASCII 's') UTF-8 string (must be valid UTF-8). Must be nul terminated and contain no other nul bytes.
// OBJECT_PATH 111    (ASCII 'o') Name of an object instance
// SIGNATURE   103    (ASCII 'g') A type signature
// ARRAY    97        (ASCII 'a')  Array
// STRUCT    114      (ASCII 'r'), 40 (ASCII '('), 41 (ASCII ')') Struct
// VARIANT   118      (ASCII 'v')     Variant type (the type of the value is part of the value itself)
// DICT_ENTRY 101     (ASCII 'e'), 123 (ASCII '{'), 125 (ASCII '}')   Entry in a dict or map (array of key-value pairs)
//            


void parseNode( Element el ){

    el.checkAllowedChilds( ID_INTF, ID_NODE );

    foreach( intf; el.getChilds( ID_INTF )){
        auto defIntf = parseInterface( intf );
        intfTree.add( defIntf );
    }
    foreach( node; el.getChilds( ID_NODE )){
        parseNode( node );
    }     
}

void unknownElement( char[][] msg ... ){
    char[200] buf;
    Stderr( layout( buf, msg ) ).newline;
}

DefInterface parseInterface( Element intf ){

    char[] elname = intf.getAttribute( "name" );
    auto defIntf = new DefInterface( elname );

    foreach( child; intf.getChilds() ){
        switch( child.getName() ){
        case ID_METH:
            defIntf.addMethod( parseMethod( child ));
            break;
        case ID_SIGN:
            defIntf.addSignal( parseSignal( child ));
            break;
        case ID_PROP:
            defIntf.addProperty( parseProperty( child ));
            break;
        case ID_ANNO:
            char[] name = child.getAttribute( "name" );
            char[] value = child.getAttribute( "value" );
            defIntf.addAnnotation( name, value );
            break;
        default:
            unknownElement( "Warning: found unknown nodetype %0 in interface", child.getName() );
            break;
        }
    }
    return defIntf;
}

DefMethod parseMethod( Element meth ){

    char[] methName = meth.getAttribute( "name" );
    auto defMeth = new DefMethod( methName );

    foreach( child; meth.getChilds() ){
        switch( child.getName() ){
        case ID_ARGU:
            defMeth.addArgument( parseArgument( child, true ));
            break;
        case ID_ANNO:
            char[] name = child.getAttribute( "name" );
            char[] value = child.getAttribute( "value" );
            defMeth.addAnnotation( name, value );
            break;
        default:
            unknownElement( "Warning: found unknown nodetype %0 in interface", child.getName() );
            break;
        }
    }
    defMeth.complete();
    return defMeth;
}

DefSignal parseSignal( Element sign ){
    char[] name = sign.getAttribute( "name" );
    auto defSign = new DefSignal( name );
    foreach( child; sign.getChilds() ){
        switch( child.getName() ){
        case ID_ARGU:
            defSign.addArgument( parseArgument( child, false ));
            break;
        case ID_ANNO:
            char[] annoName = child.getAttribute( "name" );
            char[] annoValue = child.getAttribute( "value" );
            defSign.addAnnotation( annoName, annoValue );
            break;
        default:
            unknownElement( "Warning: found unknown nodetype %0 in interface", child.getName() );
            break;
        }
    }
    return defSign;
}
DefProperty parseProperty( Element prop ){
    char[] name = prop.getAttribute( "name" );
    auto res = new DefProperty( name );
    return res;
}


DefArgument parseArgument( Element arg, bool dirDefIn ){
    char[] name = arg.getAttribute( "name" );
    DefArgument.EDir dir = dirDefIn ? DefArgument.EDir.DIR_IN : DefArgument.EDir.DIR_OUT;
    if( arg.hasAttribute( ID_DIRECTION )){
        char[] dirtxt = arg.getAttribute( ID_DIRECTION );
        switch( dirtxt ){
        case ID_DIRECTION_OUT:
            dir = DefArgument.EDir.DIR_OUT;
            break;
        case ID_DIRECTION_IN:
            dir = DefArgument.EDir.DIR_IN;
            break;
        default:
            unknownElement( "Warning: direction attribute has unknown value %0", dirtxt );
            break;
        }
    }
    char[] typetxt = arg.getAttribute( ID_TYPE );
    auto res = new DefArgument( name, typetxt, dir );
    return res;
}

char[] getIndent( int indent ){
    static char[] spaces = "                                                                                   ";
    int count = indent*4;
    assert( count >= 0 );
    assert( count < spaces.length );
    return spaces[0..count];
}

//char[][ char[] ] introspection = [ `sdklf`:`ldkfj`];

void writeIntrospectionData( Print!(char) p ){
    char[200] buf;
    char[] getArgIntrospectionData( DefArgument arg ){
        char[] res = `    <arg `;
        res ~= layout( buf, ` name=\"%0\"`, arg.mName ).dup;
        res ~= layout( buf, ` type=\"%0\"`, arg.mType ).dup;
        res ~= layout( buf, ` direction=\"%0\"`, arg.mDirection == DefArgument.EDir.DIR_IN ? ID_DIRECTION_IN : ID_DIRECTION_OUT ).dup;
        res ~= ` />\\n`;
        return res;
    }
    char[] getAnnotationIntrospectionData( char[] name, char[] value, int indent ){
        return layout( buf, `%2<annotation name=\"%0\" value=\"%1\" />\\n`, name, value, getIndent( indent ) ).dup;
    }
    char[][] getMethodIntrospectionData( DefMethod meth ){
        char[][] res;
        res ~= layout( buf, `  <method name=\"%0\">\\n`, meth.mName ).dup;
        foreach( key, value ; meth.mAnnotations ){
            res ~= getAnnotationIntrospectionData( key, value, 2 );
        }
        foreach( argu ; meth.mArguments ){
            res ~= getArgIntrospectionData( argu );
        }
        res ~= `  </method>\\n`.dup;
        return res;
    }
    char[][] getIntrospectionData( DefInterface intf ){
        char[][] res;
        res ~= layout( buf, `<interface name=\"%0\">\\n`, intf.mName ).dup;
        foreach( meth; intf.mMethods ){
            res ~= getMethodIntrospectionData( meth );
        }
        res ~= `</interface>\\n`.dup;
        return res;
    }
    p.formatln( "private void init_introspectionData(){{" );
    foreach( intfIdx, intf; intfTree.allInterfaces ){
        p.formatln( "    registerIntrospectionData(" );
        p.formatln( "        DBusInterface.{}.classinfo,", intf );
        char[][] data = getIntrospectionData( intfTree.getInterface( intf ) );
        foreach( idx, line; data ){
            if( idx < data.length-1 ){
                p.formatln( "            \"{}\"", line );
            }
            else{
                p.formatln( "            \"{}\");", line, (intfIdx<intfTree.allInterfaces.length-1)?",":"" );
            }
        }
    }
    p.formatln( "}" );
}

void error( char[][] msg... ){
    char[200] buf;
    throw new TracedException( layout( buf, msg ));
}

void writeInterfaceMethod( Print!(char) p, DefMethod meth, int indent ){
    DefArgument retArg = meth.mDRetArgument;
    DefArgument[] args = meth.mDArguments;
    char[] retType = ( retArg is null ) ? "void" : retArg.toDType();
    char[] argList;
    foreach( idx, arg; args ){
        if( idx > 0 ){
            argList ~= ",";
        }
        argList ~= " ";
        argList ~= (arg.mDirection == DefArgument.EDir.DIR_OUT) ? "out" : "in";
        argList ~= " ";
        argList ~= arg.toDType();
        argList ~= " ";
        argList ~= ( arg.mName.length == 0 ) ? "arg_" ~ toUtf8(idx) : arg.mName;
    }
    if( argList.length > 0 ){
        argList ~= " ";
    }
    p.formatln( "{}public {} {}({});", getIndent(indent), retType, meth.mName, argList );
}

void writeInterfaceSignal( Print!(char) p, DefSignal sign, int indent ){
    char[] argList;
    foreach( idx, arg; sign.mArguments ){
        if( idx > 0 ){
            argList ~= ",";
        }
        argList ~= " ";
        argList ~= arg.toDType();
    }
    if( argList.length > 0 ){
        argList ~= " ";
    }
    p.formatln( "{}public tango.core.Signal.Signal!({})* {}();", getIndent(indent), argList, sign.mName );
}

void writeInterfaceSignalDImpl( Print!(char) p, DefSignal sign, int indent ){
    char[] argList;
    foreach( idx, arg; sign.mArguments ){
        if( idx > 0 ){
            argList ~= ",";
        }
        argList ~= " ";
        argList ~= arg.toDType();
    }
    if( argList.length > 0 ){
        argList ~= " ";
    }
    p.formatln( "{}protected tango.core.Signal.Signal!({}) _{};", getIndent(indent), argList, sign.mName );
    p.formatln( "{}public tango.core.Signal.Signal!({})* {}(){{", getIndent(indent), argList, sign.mName );
    p.formatln( "{}return &_{};", getIndent(indent+1), sign.mName );
    p.formatln( "{}}", getIndent(indent) );
}

void writeInterface( Print!(char) p, char[] name, int indent ){
    p.formatln( "{}// {}", getIndent(indent), name );
    char[] nodeName = split( name, "." )[ $-1 ];
    p.formatln( "{}public interface {} {{", getIndent(indent), nodeName );
    DefInterface intf = intfTree.getInterface( name );
    if( intf !is null ){
        foreach( meth; intf.mMethods ){
            writeInterfaceMethod( p, meth, indent+1 );
        }
        foreach( sign; intf.mSignals ){
            writeInterfaceSignal( p, sign, indent+1 );
        }
        /+
        p.formatln( "{}template _StdJavaImpl(){{", getIndent(indent+1) );
        p.formatln( "{}}", getIndent(indent+1) );
        p.formatln( "{}template _StdDImpl(){{", getIndent(indent+1) );
	        p.formatln( "{}void _init(){{", getIndent(indent+2) );
	        p.formatln( "{}}", getIndent(indent+2) );
	        foreach( sign; intf.mSignals ){
	            writeInterfaceSignalDImpl( p, sign, indent+2 );
	        }
        p.formatln( "{}}", getIndent(indent+1) );
        +/
    }
    foreach( child; intfTree.getChildNames( name ) ){
        writeInterface( p, child, indent +1 );
    }
    p.formatln( "{}}", getIndent(indent) );
}

void writeHeader( Print!(char) p, char[] modName ){
    static char[] HEADER =
        "/**\n"
        " * Generated with TioLink\n"
        " * TioLink was written by Frank Benoit <benoit@tionex.de>\n"
        " * http://www.dsource.org/projects/tiolink\n"
        " *\n"
        " * File type: D programming language source code\n"
        " */\n";
    p(HEADER);
    p.formatln( "module {};", modName );
    p.newline;
    p.formatln( "public import org.freedesktop.dbus.Struct;" );
    p.formatln( "public import org.freedesktop.dbus.Variant;" );
    p.newline;
    p.formatln( "import tango.core.Signal;" );
    p.formatln( "import org.freedesktop.dbus.DBus;" );
    p.formatln( "import org.freedesktop.dbus.c.Connection : DBusConnection;" );
    p.formatln( "import org.freedesktop.dbus.c.Message : DBusMessage;" );
    p.formatln( "import org.freedesktop.dbus.c.Shared : DBusHandlerResult;" );
    p.newline;
    p.newline;
}

void writeInterfaces( Print!(char) p ){

    p.formatln( "// DBus interfaces" );
    p.formatln( "public interface DBusInterface {{" );
    foreach( child; intfTree.getChildNames( "" ) ){
        writeInterface( p, child, 1 );
    }
    p.formatln( "}" );
    p.newline;
    p.newline;

}

void writePeerMethod( Print!(char) p, DefMethod mth, int indent ){
    DefArgument retArg = mth.mDRetArgument;
    DefArgument[] args = mth.mDArguments;
    char[] retType = ( retArg is null ) ? "void" : retArg.toDType();
    char[] argList;
    foreach( idx, arg; args ){
        if( idx > 0 ){
            argList ~= ",";
        }
        argList ~= " ";
        argList ~= (arg.mDirection == DefArgument.EDir.DIR_OUT) ? "out" : "in";
        argList ~= " ";
        argList ~= arg.toDType();
        argList ~= " ";
        argList ~= ( arg.mName.length == 0 ) ? "arg_" ~ toUtf8(idx) : arg.mName;
    }
    if( argList.length > 0 ){
        argList ~= " ";
    }
    p.formatln( "{}public {} {}({}){{", getIndent(indent), retType, mth.mName, argList );
    p.formatln( "{}}", getIndent(indent) );
}

void writePeerSignal( Print!(char) p, DefSignal sig, int indent ){
}

void writePeerImpl( Print!(char) p, char[] name, int indent ){
    p.formatln( "{}// {}", getIndent(indent), name );
    char[] nodeName = split( name, "." )[ $-1 ];
    p.formatln( "{}public class {} : DBusPeerObject, DBusInterface.{} {{", getIndent(indent), nodeName, name );
    DefInterface intf = intfTree.getInterface( name );
    if( intf !is null ){
        foreach( meth; intf.mMethods ){
            writePeerMethod( p, meth, indent+1 );
        }
        foreach( sign; intf.mSignals ){
            writePeerSignal( p, sign, indent+1 );
        }
    }
    foreach( child; intfTree.getChildNames( name ) ){
        writePeerImpl( p, child, indent +1 );
    }
    p.formatln( "{}}", getIndent(indent) );
}
void writePeerImpls( Print!(char) p ){

    p.formatln( "// Peer implementations" );
    p.formatln( "public interface DBusPeers {{" );
    foreach( child; intfTree.getChildNames( "" ) ){
        writePeerImpl( p, child, 1 );
    }
    p.formatln( "}" );
    p.newline;
    p.newline;

}

void writeDBusObject( Print!(char) p ){
    p.formatln( "// DBusObject" );
    p.formatln( "public class DBusObject : DBusObjectImpl, DBusInterface.org.freedesktop.DBus.Introspectable {{" );
    p.formatln( "    this(){{" );
    p.formatln( "        super();" );
    p.formatln( "    }" );
    p.formatln( "    public char[] Introspect(){{" );
    p.formatln( "        return super.Introspect();" );
    p.formatln( "    }" );
    p.formatln( "}" );
    p.newline;
    p.newline;
}
void writeDBusPeerObject( Print!(char) p ){
    p.formatln( "// DBusPeerObject" );
    p.formatln( "public class DBusPeerObject {{" );
    p.formatln( "}" );
    p.newline;
    p.newline;
}
class TextList {
    
    private char[] separator;
    private char[] sb;
    private char[] prefix;
    private char[] postfix;
    private bool completed = false;

    public this( char[] separator ){
        this.separator = separator;
        this.prefix = "";
        this.postfix = "";
    }
    public this( char[] prefix, char[] separator, char[] postfix ){
        this.prefix = prefix;
        this.separator = separator;
        this.postfix = postfix;
    }
    
    public static TextList createParameterList(){
        return new TextList( " ", ", ", " " );
    }

    public void add( char[] value ){
        if( sb.length == 0 ){
            sb ~= prefix;
        }
        else{
            sb ~= separator;
        }
        sb ~= value;
    }
    public char[] toUtf8(){
        if( !completed ){
            if( sb.length > 0 ){
                sb ~= postfix;
            }
            completed = true;
        }
        return sb;
    }
}

void writeHandlerInterfaceMethod( Print!(char) p, DefMethod mth ){
    char[] sig = mth.mName ~ "|";
    foreach( arg; mth.mArguments ){
        if( arg.mDirection == DefArgument.EDir.DIR_IN ){
            sig ~= arg.mType;
        }
    }

    DefArgument retArg = mth.mDRetArgument;
    DefArgument[] args = mth.mDArguments;

    p.formatln("        case \"{}\":", sig );
    p.formatln("            {{" );
    TextList callParams = TextList.createParameterList();
    TextList callTypes = TextList.createParameterList();
    TextList callInTypes = TextList.createParameterList();
    TextList callOutTypes = TextList.createParameterList();
    int idxi, idxo;
    char[] retAssign = "";
    char[] retVar = "";
    bool hasOutputs = false;
    bool hasInputs = false;
    foreach( idx, arg; mth.mArguments ){
        callTypes.add( arg.toDType());
        if( arg.mDirection == DefArgument.EDir.DIR_IN ){
            hasInputs = true;
            callInTypes.add( arg.toDType());
            callParams.add( "pi.t["~toUtf8(idxi)~"]" );
            idxi++;
        }
        else{
            hasOutputs = true;
            callOutTypes.add( arg.toDType());

            char[] outArg = "po.t["~toUtf8(idxo)~"]";
            idxo++;
            if( arg is mth.mDRetArgument ){
                retVar = outArg;
                retAssign = retVar~" = ";
            }
            else{
                callParams.add( outArg );
            }
        }
    }
    if( hasInputs ){
        p.formatln("                Struct!({0}) pi = getCallValues!({0})( message );", callInTypes.toUtf8() );
    }
    if( hasOutputs ){
        p.formatln("                Struct!({0}) po;" , callOutTypes.toUtf8() );
    }
    p.formatln("                {}o.{}({});", retAssign, mth.mName, callParams.toUtf8() );
    if( hasOutputs ){
        p.formatln("                sendReplyData!({})( conn, message, po );", callOutTypes.toUtf8() );
    }
    else{
        p.formatln("                sendReply( conn, message );" );
    }
    p.formatln("            }" );
    p.formatln("            break;" );


}
void writeHandlerInterfaceSignal( Print!(char) p, DefSignal sig ){
    char[] signature = sig.mName ~ ">";
    foreach( arg; sig.mArguments ){
        signature ~= arg.mType;
    }

    DefArgument[] args = sig.mArguments;

    p.formatln("        case \"{}\":", signature );
    p.formatln("            {{" );
    TextList callParams = TextList.createParameterList();
    TextList callInTypes = TextList.createParameterList();
    int idxi;
    char[] retAssign = "";
    char[] retVar = "";
    bool hasInputs = false;
    foreach( idx, arg; sig.mArguments ){
        hasInputs = true;
        callInTypes.add( arg.toDType());
        callParams.add( "pi.t["~toUtf8(idxi)~"]" );
        idxi++;
    }
    if( hasInputs ){
        p.formatln("                Struct!({0}) pi = getCallValues!({0})( message );", callInTypes.toUtf8() );
    }
    p.formatln("                o.{}().opCall({});", sig.mName, callParams.toUtf8() );
    p.formatln("            }" );
    p.formatln("            break;" );


}
void writeHandlerInterface( Print!(char) p, DefInterface intf ){
    p.formatln("private DBusHandlerResult intfHandler__{}( "
        "DBusConnection* conn, DBusMessage* message, void* user_data ){{",
        replace( intf.mName.dup, '.', '_') );
    p.formatln("    DBusInterface.{} o = cast(DBusInterface.{0})cast(Object)user_data;", intf.mName );
    p.formatln("    if( o is null || !checkIntf( \"{}\", message) )", intf.mName );
    p.formatln("        return DBusHandlerResult.DBUS_HANDLER_RESULT_NOT_YET_HANDLED;" );
    p.formatln("");
    p.formatln("    try{{");
    p.formatln("        char[METHOD_SIG_MAXLENGTH] buf;");
    p.formatln("        switch( methodSignature( message, buf ) ){{");
    foreach( DefMethod mth ; intf.mMethods ){
        writeHandlerInterfaceMethod( p, mth );
    }
    foreach( DefSignal sig ; intf.mSignals ){
        writeHandlerInterfaceSignal( p, sig );
    }
    p.formatln("        default:" );
    p.formatln("            return DBusHandlerResult.DBUS_HANDLER_RESULT_NOT_YET_HANDLED;");
    p.formatln("        }");
    p.formatln("    }");
    p.formatln("    catch( Exception e ){{");
    p.formatln("        sendException( conn, message, e );" );
    p.formatln("    }");
    p.formatln("    return DBusHandlerResult.DBUS_HANDLER_RESULT_HANDLED;");
    p.formatln("}");
    p.newline;
}
void writeHandlers( Print!(char) p ){


    foreach( intfIdx, intf; intfTree.allInterfaces ){
        writeHandlerInterface( p, intfTree.getInterface( intf ));
    }

    p.formatln( "private void init_handlers(){{" );
    foreach( intfIdx, intf; intfTree.allInterfaces ){
        p.formatln( "    registerHandler(" );
        p.formatln( "        DBusInterface.{}.classinfo,", intf );
        p.formatln( "            & intfHandler__{} );", replace( intf.dup, '.', '_' ));
    }
    p.formatln( "}" );
    p.newline;

}
void writeStaticInit( Print!(char) p ){
    p.formatln( "static this(){{" );
    p.formatln( "    init_introspectionData();" );
    p.formatln( "    init_handlers();" );
    p.formatln( "}" );
    p.newline;
}

int main(char[][] args) {
    try{
        if( args.length != 3 ){
            printSyntax();
            throw new TerminateException(1);
        }
        char[] introxml = args[1];
        char[] modname  = args[2];
        intfTree = new IntfTree();
        auto root = parse( introxml );
        createStdInterfaces();
        parseNode( root );
        Print!(char) output = Stdout;
        writeHeader( output, modname );
        writeInterfaces( output );
        //writePeerImpls( output );
        writeDBusObject( output );
        //writeDBusPeerObject( output );
        writeIntrospectionData( output );
        writeHandlers( output );
        writeStaticInit( output );
    }
    catch( TerminateException e ){
        return e.code;
    }
    return(0);
}


void printSyntax() {
    Stdout.formatln("dbus-createinterface <introspectionsdata.xml> <modulename>");
}