/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Sun Microsystems,
 * Inc. Portions created by Sun are
 * Copyright (C) 1999 Sun Microsystems, Inc. All
 * Rights Reserved.
 *
 * Contributor(s):
 *    Michael Allen (michael.allen@sun.com)
 *    Frank Mitchell (frank.mitchell@sun.com)
 */

/*
 * Generate Java interfaces from XPIDL.
 */

#include "xpidl.h"
#include <ctype.h>
#include <glib.h>


struct java_priv_data {
    GHashTable *typedefTable;
};

#define TYPEDEFS(state)     (((struct java_priv_data *)state->priv)->typedefTable)

static gboolean
write_classname_iid_define(FILE *file, const char *className)
{
    const char *iidName;
    if (className[0] == 'n' && className[1] == 's') {
        /* backcompat naming styles */
        fputs("NS_", file);
        iidName = className + 2;
    } else {
        iidName = className;
    }

    while (*iidName) {
        fputc(toupper(*iidName++), file);
    }

    fputs("_IID", file);
    return TRUE;
}

static gboolean
java_prolog(TreeState *state)
{
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    const char *basename;
    const char *ext;
#endif

    state->priv = calloc(1, sizeof(struct java_priv_data));
    if (!state->priv)
        return FALSE;
    TYPEDEFS(state) = 0;
    TYPEDEFS(state) = g_hash_table_new(g_str_hash, g_str_equal);
    if (!TYPEDEFS(state)) {
        /* XXX report error */
        free(state->priv);
        return FALSE;
    }

    /*
     * First pass
     */

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    basename = xpidl_basename(state->real_outname ? state->real_outname : state->basename);
    ext = strrchr(basename, '.');
    if (!ext)
        ext = strchr(basename, '\0');
    fprintf(state->file,
            "/**\n"
            " * NOTE: THIS IS A GENERATED FILE. PLEASE CONSULT THE ORIGINAL IDL FILE\n"
            " * FOR THE FULL DOCUMENTATION AND LICENSE.\n"
            " *\n"
            " * @see <a href=\"http://lxr.mozilla.org/mozilla/search?string=interface+%.*s\">\n"
            " **/\n"
            "\n"
            "package org.mozilla.interfaces;\n\n"
            "import java.math.BigInteger;\n\n"
            "\n"
            , ext - basename >= 19 ? 19 : (int)(ext - basename), basename);
    g_free(basename);
#else
    fputs("/*\n * ************* DO NOT EDIT THIS FILE ***********\n",
          state->file);

    fprintf(state->file,
            " *\n * This file was automatically generated from %s.idl.\n",
            state->basename);

    fputs(" */\n\n", state->file);
#endif

    return TRUE;
}

static gboolean
java_epilog(TreeState *state)
{
    /* points to other elements of the tree, so just destroy the table */
    g_hash_table_destroy(TYPEDEFS(state));
    free(state->priv);
    state->priv = NULL;

#ifndef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    /*
     * Last pass
     */

    fprintf(state->file, "\n/*\n * end\n */\n");
#endif

    return TRUE;
}

static gboolean
forward_declaration(TreeState *state)
{
    /*
     * Java doesn't need forward declarations unless the declared
     * class resides in a different package.
     */
#if 0
    IDL_tree iface = state->tree;
    const char *className = IDL_IDENT(IDL_FORWARD_DCL(iface).ident).str;
    const char *pkgName = "org.mozilla.xpcom";
    if (!className)
        return FALSE;
    /* XXX: Get package name and compare */
    fprintf(state->file, "import %s.%s;\n", pkgName, className);
#endif
    return TRUE;
}


static gboolean
interface_declaration(TreeState *state)
{
    IDL_tree interface = state->tree;
    IDL_tree iterator = NULL;
    char *interface_name = IDL_IDENT(IDL_INTERFACE(interface).ident).str;
    const char *iid = NULL;

    if (!verify_interface_declaration(interface))
        return FALSE;
#ifndef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    /*
     * Write out JavaDoc comment
     */

    fprintf(state->file, "\n/**\n * Interface %s\n", interface_name);
#endif

#ifndef LIBIDL_MAJOR_VERSION
    iid = IDL_tree_property_get(interface, "uuid");
#else
    iid = IDL_tree_property_get(IDL_INTERFACE(interface).ident, "uuid");
#endif

#ifndef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    if (iid != NULL) {
        fprintf(state->file, " *\n * IID: 0x%s\n */\n\n", iid);
    } else {
        fputs(" */\n\n", state->file);
    }
#endif

    /*
     * Write "public interface <foo>"
     */

    fprintf(state->file, "public interface %s ", interface_name);

    /*
     * Check for inheritence, and iterator over the inherited names,
     * if any.
     */

    if ((iterator = IDL_INTERFACE(interface).inheritance_spec)) {
        fputs("extends ", state->file);

        do {

            fprintf(state->file, "%s",
                    IDL_IDENT(IDL_LIST(iterator).data).str);

            if (IDL_LIST(iterator).next) {
                fputs(", ", state->file);
            }
        } while ((iterator = IDL_LIST(iterator).next));

    }

    fputs("\n{\n", state->file);

    if (iid) {
        /*
         * Write interface constants for IID
         */

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
        fputs("  public static final String ", state->file);
#else
        fputs("    public static final String ", state->file);
#endif

        /* XXX s.b just "IID" ? */
        if (!write_classname_iid_define(state->file, interface_name)) {
            return FALSE;
        }

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
        fputs(" =\n    \"{", state->file);
        while (*iid) {
            fputc(tolower(*iid++), state->file);
        }
        fputs("}\";\n", state->file);

#else
        fprintf(state->file, "_STRING =\n        \"%s\";\n\n", iid);

        fputs("    public static final nsID ", state->file);

        /* XXX s.b just "IID" ? */
        if (!write_classname_iid_define(state->file, interface_name)) {
            return FALSE;
        }

        fprintf(state->file, " =\n        new nsID(\"%s\");\n\n", iid);
#endif
    }

    /*
     * Advance the state of the tree, go on to process more
     */

    state->tree = IDL_INTERFACE(interface).body;

    if (state->tree && !xpidl_process_node(state)) {
        return FALSE;
    }


    fputs("\n}\n", state->file);

    return TRUE;
}

static gboolean
process_list(TreeState *state)
{
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    /* To make the diffing simple, group the constants, methods and attributes. */
    IDL_tree list = state->tree;
    IDL_tree iter;
    for (iter = list; iter; iter = IDL_LIST(iter).next) {
        if (IDL_NODE_TYPE(IDL_LIST(iter).data) == IDLN_CONST_DCL) {
            state->tree = IDL_LIST(iter).data;
            if (!xpidl_process_node(state))
                return FALSE;
        }
    }
    for (iter = list; iter; iter = IDL_LIST(iter).next) {
        if (IDL_NODE_TYPE(IDL_LIST(iter).data) == IDLN_ATTR_DCL) {
            state->tree = IDL_LIST(iter).data;
            if (!xpidl_process_node(state))
                return FALSE;
        }
    }
    for (iter = list; iter; iter = IDL_LIST(iter).next) {
        if (IDL_NODE_TYPE(IDL_LIST(iter).data) == IDLN_OP_DCL) {
            state->tree = IDL_LIST(iter).data;
            if (!xpidl_process_node(state))
                return FALSE;
        }
    }
    for (iter = list; iter; iter = IDL_LIST(iter).next) {
        if (   IDL_NODE_TYPE(IDL_LIST(iter).data) != IDLN_CONST_DCL
            && IDL_NODE_TYPE(IDL_LIST(iter).data) != IDLN_OP_DCL
            && IDL_NODE_TYPE(IDL_LIST(iter).data) != IDLN_ATTR_DCL ) {
            state->tree = IDL_LIST(iter).data;
            if (!xpidl_process_node(state))
                return FALSE;
        }
    }

#else
    IDL_tree iter;
    for (iter = state->tree; iter; iter = IDL_LIST(iter).next) {
        state->tree = IDL_LIST(iter).data;
        if (!xpidl_process_node(state))
            return FALSE;
    }
#endif
    return TRUE;
}

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
static gboolean
interface_declaration_wrapper(TreeState *state)
{
    IDL_tree interface = state->tree;
    char *interface_name = IDL_IDENT(IDL_INTERFACE(interface).ident).str;
    FILE *org_file = state->file;
    char *org_name = state->real_outname;
    void *org_priv = state->priv;
    gboolean rc;

    /*
     * Skip non-scriptable interfaces.
     */
    if (   !IDL_tree_property_get(IDL_INTERFACE(interface).ident, "scriptable")
         && strcmp(interface_name, "nsIAppShell") )
        return TRUE;

    /*
     * GROSS HACK: If the interface isn't the same as the file name,
     *             temporarily switch output file.
     */
    if (state->real_outname) {
        const char *basename = xpidl_basename(state->real_outname);
        const char *ext      = strrchr(basename, '.');
        if (   ext
            && !strcmp(ext, ".java")
            && (   strncmp(interface_name, basename, ext - basename)
                || interface_name[ext - basename] != '.') ) {
            size_t needed = strlen(state->real_outname) + strlen(interface_name) + strlen(".java") + 4;
            char  *tmp = malloc(needed);
            if (basename != state->real_outname)
                sprintf(tmp,"%.*s/%s.java", (int)(basename - state->real_outname - 1), state->real_outname, interface_name);
            else
                sprintf(tmp,"%s.java", interface_name);
            state->file = fopen(tmp, "w");
            if (!state->file) {
                perror("error opening output file");
                state->file = org_file;
                free(tmp);
                return FALSE;
            }
            state->real_outname = tmp;
            java_prolog(state);
        }
        g_free(basename);
    }

    rc = interface_declaration(state);

    if (state->file != org_file) {
        java_epilog(state);
        fclose(state->file);
        free(state->real_outname);
        state->file = org_file;
        state->real_outname = org_name;
        state->priv = org_priv;
    }
    return rc;
}
#endif /* VBOX_XPIDL_EMULATE_GENJIFACES */

static gboolean
xpcom_to_java_type (TreeState *state)
{
    if (!state->tree) {
        fputs("Object", state->file);
        return TRUE;
    }

    switch(IDL_NODE_TYPE(state->tree)) {

    case IDLN_TYPE_INTEGER: {

        switch(IDL_TYPE_INTEGER(state->tree).f_type) {

        case IDL_INTEGER_TYPE_SHORT:
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
            if (IDL_TYPE_INTEGER(state->tree).f_signed)
                fputs("short", state->file);
            else
                fputs("int", state->file);
#else
            fputs("short", state->file);
#endif
            break;

        case IDL_INTEGER_TYPE_LONG:
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
            if (IDL_TYPE_INTEGER(state->tree).f_signed)
                fputs("int", state->file);
            else
                fputs("long", state->file);
#else
            fputs("int", state->file);
#endif
            break;

        case IDL_INTEGER_TYPE_LONGLONG:
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
            if (IDL_TYPE_INTEGER(state->tree).f_signed)
                fputs("long", state->file);
            else
                fputs("double", state->file);
#else
            fputs("long", state->file);
#endif
            break;

        default:
            g_error("   Unknown integer type: %d\n",
                    IDL_TYPE_INTEGER(state->tree).f_type);
            return FALSE;

        }

        break;
    }

    case IDLN_TYPE_CHAR:
    case IDLN_TYPE_WIDE_CHAR:
        fputs("char", state->file);
        break;

    case IDLN_TYPE_WIDE_STRING:
    case IDLN_TYPE_STRING:
        fputs("String", state->file);
        break;

    case IDLN_TYPE_BOOLEAN:
        fputs("boolean", state->file);
        break;

    case IDLN_TYPE_OCTET:
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
        fputs("short", state->file);
#else
        fputs("byte", state->file);
#endif
        break;

    case IDLN_TYPE_FLOAT:
        switch(IDL_TYPE_FLOAT(state->tree).f_type) {

        case IDL_FLOAT_TYPE_FLOAT:
            fputs("float", state->file);
            break;

        case IDL_FLOAT_TYPE_DOUBLE:
            fputs("double", state->file);
            break;

        default:
            g_error("    Unknown floating point typ: %d\n",
                    IDL_NODE_TYPE(state->tree));
            break;
        }
        break;


    case IDLN_IDENT:
        if (IDL_NODE_UP(state->tree) &&
            IDL_NODE_TYPE(IDL_NODE_UP(state->tree)) == IDLN_NATIVE) {
            const char *user_type = IDL_NATIVE(IDL_NODE_UP(state->tree)).user_type;
            if (strcmp(user_type, "void") == 0) {
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
                fputs("nsISupports", state->file);
#else
                fputs("Object", state->file);
#endif
            }
            else if (strcmp(user_type, "nsID") == 0 ||
                     strcmp(user_type, "nsIID") == 0 ||
                     strcmp(user_type, "nsCID") == 0) {
                /* XXX: s.b test for "iid" attribute */
                /* XXX: special class for nsIDs */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
                fputs("String", state->file);
#else
                fputs("nsID", state->file);
#endif
            }
            else {
                /* XXX: special class for opaque types */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
                fputs("String", state->file);
#else
                fputs("OpaqueValue", state->file);
#endif
            }
        } else {
            const char *ident_str = IDL_IDENT(state->tree).str;

            /* XXX: big kludge; s.b. way to match to typedefs */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
            if (strcmp(ident_str, "PRInt8") == 0) {
                fputs("byte", state->file);
            }
            else if (strcmp(ident_str, "PRInt16") == 0 ||
                     strcmp(ident_str, "PRUint8") == 0) {
                fputs("short", state->file);
            }
            else if (strcmp(ident_str, "PRInt32") == 0 ||
                     strcmp(ident_str, "PRUint16") == 0) {
                fputs("int", state->file);
            }
            else if (strcmp(ident_str, "PRInt64") == 0 ||
                     strcmp(ident_str, "PRUint32") == 0 ||
                     strcmp(ident_str, "PRThreadPriority") == 0 ||
                     strcmp(ident_str, "PRThreadScope")    == 0 ||
                     strcmp(ident_str, "PRThreadState")    == 0) {
                fputs("long", state->file);
            }
            else if (strcmp(ident_str, "PRUint64") == 0) {
                fputs("double", state->file);
            }
#else
            if (strcmp(ident_str, "PRInt8") == 0 ||
                strcmp(ident_str, "PRUint8") == 0) {
                fputs("byte", state->file);
            }
            else if (strcmp(ident_str, "PRInt16") == 0 ||
                     strcmp(ident_str, "PRUint16") == 0) {
                fputs("short", state->file);
            }
            else if (strcmp(ident_str, "PRInt32") == 0 ||
                     strcmp(ident_str, "PRUint32") == 0) {
                fputs("int", state->file);
            }
            else if (strcmp(ident_str, "PRInt64") == 0 ||
                     strcmp(ident_str, "PRUint64") == 0) {
                fputs("long", state->file);
            }
#endif
            else if (strcmp(ident_str, "PRBool") == 0) {
                fputs("boolean", state->file);
            }
            else if (strcmp(ident_str, "nsrefcnt") == 0) {
                fputs("int", state->file);
            }
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
            /* XXX: Use find_underlying_type instead? */
            else if (   strcmp(ident_str, "nsresult") == 0
                     || strcmp(ident_str, "size_t") == 0) {
                fputs("long", state->file);
            }
            else if (   strcmp(ident_str, "PRTime") == 0) {
                fputs("double", state->file);
            }
            /* In Javaconnect, we handle weak references internally; no need for the
              |nsIWeakReference| interface.  So just return |nsISupports|. */
            else if (strcmp(ident_str, "nsIWeakReference") == 0) {
                fputs("nsISupports", state->file);
            }
#endif
            else {
                IDL_tree real_type =
                    g_hash_table_lookup(TYPEDEFS(state), ident_str);

                if (real_type) {
                    IDL_tree orig_tree = state->tree;

                    state->tree = real_type;
                    xpcom_to_java_type(state);

                    state->tree = orig_tree;
                }
                else {
                    fputs(ident_str, state->file);
                }
            }
        }

        break;

    case IDLN_TYPE_ENUM:
    case IDLN_TYPE_OBJECT:
    default:
        g_error("    Unknown type: %d\n",
                IDL_TYPE_FLOAT(state->tree).f_type);
        break;
    }

    return TRUE;

}

static gboolean
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
xpcom_to_java_param(TreeState *state, unsigned nparam)
#else
xpcom_to_java_param(TreeState *state)
#endif
{
    IDL_tree param = state->tree;
    state->tree = IDL_PARAM_DCL(param).param_type_spec;

    /*
     * Put in type of parameter
     */

    if (!xpcom_to_java_type(state)) {
        return FALSE;
    }

    /*
     * If the parameter is out or inout, make it a Java array of the
     * appropriate type
     */

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
    /* XXX: Causes nsILineInputStream::readLine(String[] arg1) where genjifaces drops the []. */
#endif
    if (IDL_PARAM_DCL(param).attr != IDL_PARAM_IN) {
        fputs("[]", state->file);
    }
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
    /*XXX: nsIConsoleService::getMessageArray ends up with [][] arg1... */
    /*else*/ if (IDL_tree_property_get(IDL_PARAM_DCL(param).simple_declarator, "array")) {
        fputs("[]", state->file);
    }
#endif

    /*
     * Put in name of parameter
     */

    fputc(' ', state->file);

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    fprintf(state->file, "arg%u", nparam+1);
#else
    fputs(IDL_IDENT(IDL_PARAM_DCL(param).simple_declarator).str, state->file);
#endif

    return TRUE;
}

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
static gboolean is_java_keyword(char ch0, const char *name)
{
    static const char * const kJavaKeywords[] = {
        "abstract", "default", "if"        , "private"     , "this"     ,
        "boolean" , "do"     , "implements", "protected"   , "throw"    ,
        "break"   , "double" , "import",     "public"      , "throws"   ,
        "byte"    , "else"   , "instanceof", "return"      , "transient",
        "case"    , "extends", "int"       , "short"       , "try"      ,
        "catch"   , "final"  , "interface" , "static"      , "void"     ,
        "char"    , "finally", "long"      , "strictfp"    , "volatile" ,
        "class"   , "float"  , "native"    , "super"       , "while"    ,
        "const"   , "for"    , "new"       , "switch"      ,
        "continue", "goto"   , "package"   , "synchronized",
        "assert"  ,  /* added in Java 1.4 */
        "enum"    ,  /* added in Java 5.0 */
        "clone"   ,  /* clone is a member function of java.lang.Object */
        "finalize"   /* finalize is a member function of java.lang.Object */
    };
    unsigned i;
    for (i = 0; i < sizeof(kJavaKeywords) / sizeof(kJavaKeywords[0]); i++) {
        if (kJavaKeywords[i][0] == ch0 && !strcmp(&kJavaKeywords[i][1], &name[1])) {
            return TRUE;
        }
    }
    return FALSE;
}
#endif

static gboolean
type_declaration(TreeState *state)
{
    /*
     * Unlike C, Java has no type declaration directive.
     * Instead, we record the mapping, and look up the actual type
     * when needed.
     */
    IDL_tree type = IDL_TYPE_DCL(state->tree).type_spec;
    IDL_tree dcls = IDL_TYPE_DCL(state->tree).dcls;

    /* XXX: check for illegal types */

    g_hash_table_insert(TYPEDEFS(state),
                        IDL_IDENT(IDL_LIST(dcls).data).str,
                        type);

    return TRUE;
}

static gboolean
method_declaration(TreeState *state)
{
    /* IDL_tree method_tree = state->tree; */
    struct _IDL_OP_DCL *method = &IDL_OP_DCL(state->tree);
    gboolean method_notxpcom =
        (IDL_tree_property_get(method->ident, "notxpcom") != NULL);
    gboolean method_noscript =
        (IDL_tree_property_get(method->ident, "noscript") != NULL);
    IDL_tree iterator = NULL;
    IDL_tree retval_param = NULL;
    const char *method_name = IDL_IDENT(method->ident).str;
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    unsigned nparam = 0;
#endif

    if (!verify_method_declaration(state->tree))
        return FALSE;

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
    /*
     * Skip most (todo) non-scriptable and not-xpcom methods.
     */
    if (method_noscript || method_notxpcom) {
        return TRUE;
    }
#endif

    fputc('\n', state->file);
#ifndef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    xpidl_write_comment(state, 4);
#endif

    /*
     * Write beginning of method declaration
     */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    fputs("  ", state->file);
#else
    fputs("    ", state->file);
#endif
    if (!method_noscript) {
        /* Nonscriptable methods become package-protected */
        fputs("public ", state->file);
    }

    /*
     * Write return type
     * Unlike C++ headers, Java interfaces return the declared
     * return value; an exception indicates XPCOM method failure.
     */
    if (method_notxpcom || method->op_type_spec) {
        state->tree = method->op_type_spec;
        if (!xpcom_to_java_type(state)) {
            return FALSE;
        }
    } else {
        /* Check for retval attribute */
        for (iterator = method->parameter_dcls; iterator != NULL;
             iterator = IDL_LIST(iterator).next) {

            IDL_tree original_tree = state->tree;

            state->tree = IDL_LIST(iterator).data;

            if (IDL_tree_property_get(IDL_PARAM_DCL(state->tree).simple_declarator,
                                      "retval")) {
                retval_param = iterator;

                state->tree = IDL_PARAM_DCL(state->tree).param_type_spec;

                /*
                 * Put in type of parameter
                 */

                if (!xpcom_to_java_type(state)) {
                    return FALSE;
                }
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
                if (IDL_tree_property_get(IDL_PARAM_DCL(IDL_LIST(iterator).data).simple_declarator, "array")) {
                    fputs("[]", state->file);
                }
#endif
            }

            state->tree = original_tree;
        }

        if (retval_param == NULL) {
            fputs("void", state->file);
        }
    }

    /*
     * Write method name
     */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
    if (is_java_keyword(tolower(method_name[0]), method_name)) {
        fprintf(state->file, " %c%s_(", tolower(method_name[0]), method_name + 1);
    } else {
        fprintf(state->file, " %c%s(", tolower(method_name[0]), method_name + 1);
    }
#else
    fprintf(state->file, " %c%s(", tolower(method_name[0]), method_name + 1);
#endif

    /*
     * Write parameters
     */
    for (iterator = method->parameter_dcls; iterator != NULL;
         iterator = IDL_LIST(iterator).next) {

        /* Skip "retval" */
        if (iterator == retval_param) {
            continue;
        }

        if (iterator != method->parameter_dcls) {
            fputs(", ", state->file);
        }

        state->tree = IDL_LIST(iterator).data;

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
        if (!xpcom_to_java_param(state, nparam++)) {
#else
        if (!xpcom_to_java_param(state)) {
#endif
            return FALSE;
        }
    }

    fputs(")", state->file);

    if (method->raises_expr) {
        IDL_tree iter = method->raises_expr;
        IDL_tree dataNode = IDL_LIST(iter).data;

        fputs(" throws ", state->file);
        fputs(IDL_IDENT(dataNode).str, state->file);
        iter = IDL_LIST(iter).next;

        while (iter) {
            dataNode = IDL_LIST(iter).data;
            fprintf(state->file, ", %s", IDL_IDENT(dataNode).str);
            iter = IDL_LIST(iter).next;
        }
    }

    fputs(";\n", state->file);

    return TRUE;

}


static gboolean
constant_declaration(TreeState *state)
{
    struct _IDL_CONST_DCL *declaration = &IDL_CONST_DCL(state->tree);
    const char *name = IDL_IDENT(declaration->ident).str;
    IDL_tree real_type;

    if (!verify_const_declaration(state->tree))
        return FALSE;

    /* Could be a typedef; try to map it to the real type. */
    real_type = find_underlying_type(declaration->const_type);
    real_type = real_type ? real_type : declaration->const_type;

    fputc('\n', state->file);
#ifndef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    xpidl_write_comment(state, 4);
#endif

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
# ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    fputs("  public static final ", state->file);
# else
    fputs("    public static final ", state->file);
# endif
    if (IDL_TYPE_INTEGER(real_type).f_type == IDL_INTEGER_TYPE_LONG) {
        if (IDL_TYPE_INTEGER(real_type).f_signed)
            fprintf(state->file, "int %s = %"   IDL_LL "d;\n",  name, IDL_INTEGER(declaration->const_exp).value);
        else
            fprintf(state->file, "long %s = %"  IDL_LL "uL;\n", name, IDL_INTEGER(declaration->const_exp).value);
    } else {
        if (IDL_TYPE_INTEGER(real_type).f_signed)
            fprintf(state->file, "short %s = %" IDL_LL "d;\n",  name, IDL_INTEGER(declaration->const_exp).value);
        else
            fprintf(state->file, "int %s = %"   IDL_LL "u;\n",  name, IDL_INTEGER(declaration->const_exp).value);
    }
#else  /* !VBOX_XPIDL_EMULATE_GENJIFACES */
    fprintf(state->file, "    public static final %s %s = %d;\n",
            (IDL_TYPE_INTEGER(real_type).f_type == IDL_INTEGER_TYPE_LONG
             ? "long" : "short"),
            name, (int) IDL_INTEGER(declaration->const_exp).value);
#endif /* !VBOX_XPIDL_EMULATE_GENJIFACES */

    return TRUE;

}

#define ATTR_IDENT(tree) (IDL_IDENT(IDL_LIST(IDL_ATTR_DCL((tree)).simple_declarations).data))
#define ATTR_PROPS(tree) (IDL_LIST(IDL_ATTR_DCL((tree)).simple_declarations).data)
#define ATTR_TYPE_DECL(tree) (IDL_ATTR_DCL((tree)).param_type_spec)


static gboolean
attribute_declaration(TreeState *state)
{
    gboolean read_only = IDL_ATTR_DCL(state->tree).f_readonly;
    char *attribute_name = ATTR_IDENT(state->tree).str;

    gboolean method_noscript =
        (IDL_tree_property_get(ATTR_PROPS(state->tree), "noscript") != NULL);

#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
    /*
     * Skip most non-scriptable attributes.
     */
    if (method_noscript) {
        return TRUE;
    }
#endif

#if 0
    /*
     * Disabled here because I can't verify this check against possible
     * users of the java xpidl backend.
     */
    if (!verify_attribute_declaration(state->tree))
        return FALSE;
#endif

    /* Comment */
    fputc('\n', state->file);
#ifndef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    xpidl_write_comment(state, 4);
#endif

    state->tree = ATTR_TYPE_DECL(state->tree);

    /*
     * Write access permission ("public" unless nonscriptable)
     */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
    fputs("  ", state->file);
#else
    fputs("    ", state->file);
#endif
    if (!method_noscript) {
        fputs("public ", state->file);
    }

    /*
     * Write the proper Java return value for the get operation
     */
    if (!xpcom_to_java_type(state)) {
        return FALSE;
    }

    /*
     * Write the name of the accessor ("get") method.
     */
    fprintf(state->file, " get%c%s();\n",
            toupper(attribute_name[0]), attribute_name + 1);


    if (!read_only) {
        /* Nonscriptable methods become package-protected */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
        fputs("\n  ", state->file);
#else
        fputs("    ", state->file);
#endif
        if (!method_noscript) {
            fputs("public ", state->file);
        }

        /*
         * Write attribute access method name and return type
         */
        fprintf(state->file, "void set%c%s(",
                toupper(attribute_name[0]),
                attribute_name+1);

        /*
         * Write the proper Java type for the set operation
         */
        if (!xpcom_to_java_type(state)) {
            return FALSE;
        }

        /*
         * Write the name of the formal parameter.
         */
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES_DIFF
        fputs(" arg1);\n", state->file);
#else
        fputs(" value);\n", state->file);
#endif
    }

    return TRUE;
}


static gboolean
enum_declaration(TreeState *state)
{
    XPIDL_WARNING((state->tree, IDL_WARNING1,
                   "enums not supported, enum \'%s\' ignored",
                   IDL_IDENT(IDL_TYPE_ENUM(state->tree).ident).str));
    return TRUE;
}

backend *
xpidl_java_dispatch(void)
{
    static backend result;
    static nodeHandler table[IDLN_LAST];
    static gboolean initialized = FALSE;

    result.emit_prolog = java_prolog;
    result.emit_epilog = java_epilog;

    if (!initialized) {
#ifdef VBOX_XPIDL_EMULATE_GENJIFACES
        table[IDLN_INTERFACE] = interface_declaration_wrapper;
#else
        table[IDLN_INTERFACE] = interface_declaration;
#endif
        table[IDLN_LIST] = process_list;

        table[IDLN_OP_DCL] = method_declaration;
        table[IDLN_ATTR_DCL] = attribute_declaration;
        table[IDLN_CONST_DCL] = constant_declaration;

        table[IDLN_TYPE_DCL] = type_declaration;
        table[IDLN_FORWARD_DCL] = forward_declaration;

        table[IDLN_TYPE_ENUM] = enum_declaration;

        initialized = TRUE;
    }

    result.dispatch_table = table;
    return &result;
}

