[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: OPP patch and new build.xml file...



David Li wrote:

> > > Being a directory based task in Ant has an advantage of specify a
> > > fileset pattern (for example, for all the *Impl.java under src
> > > directory). It beats manually maintaining the list of Impl file to
> > > generate the proxy.
> >
> > ok, ok... I'm convinced ;)
>
> Will get on the Ant task for OPP hacking this weekend.

Hi,

maybe my little Ant 1.3 task for OPP can help you? Please check the resulting 
proxy code for correctness, I did not do much testing. It does not support 
ODMG proxy generation. As you can see in the code, I am working on replacing 
the DxLib with Java 2 collections.

Jörg

-- 
Jörg Prante
Sevenval AG (HRB 32757) e-business marketing technologies
D-50667 Köln . Alter Markt 36-42
Fon +49 221 65 00 70 . Fax +49 221 42 49 891 . Mobile +49 175 466 19 18
http://www.sevenval.de . joerg@7val.com
/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/**
 * You can redistribute this software and/or modify it under the terms of
 * the Ozone Library License version 1 published by ozone-db.org.
 *
 * The original code and portions created by SMB are
 * Copyright (C) 1997-2001 by SMB GmbH. All rights reserved.
 */

package com.sevenval.util.ozonedb;

import java.io.PrintWriter;
import java.io.File;
import java.io.FileReader;
import java.io.LineNumberReader;
import java.io.FileOutputStream;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.TreeSet;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;

import org.apache.regexp.RE;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory;
import org.apache.tools.ant.util.GlobPatternMapper;
import org.apache.tools.ant.util.SourceFileScanner;

import org.ozoneDB.OzoneRemote;

/**
 * Ant task for generating Ozone proxies.
 *
 * This task is extending the Javac task and
 * can take the following arguments:
 * <ul>
 * <li>sourcedir
 * <li>package
 * <li>destdir
 * <li>deprecation
 * <li>classpath
 * <li>bootclasspath
 * <li>extdirs
 * <li>optimize
 * <li>debug
 * <li>encoding
 * <li>target
 * <li>depend
 * <li>vebose
 * <li>failonerror
 * <li>includeantruntime
 * <li>includejavaruntime
 * </ul>
 * Of these arguments, the <b>sourcedir</b>, <b>package</b>, and
 * <b>destdir</b> are required.
 * <p>
 * When this task executes, it will recursively scan the sourcedir and
 * destdir looking for Ozone Proxy Impl source files to compile.
 * This task makes its compile decision based on timestamp.
 *
 * Declare the task with:
 * <pre><code>
 *     &lt;taskdef name="opp" classname="myozoneutilpackage.AntProxyGenerator" />
 * </code></pre>
 *
 * Invoke the proxy generator with:
 * <pre><code>
 *     &lt;!-- Ant OPP example -->
 *     &lt;target name="ozoneproxies" depends="prepare">
 *         &lt;opp srcdir="${build.src}/mypackage/ozone"
 *            package="mypackage.ozone"
 *            destdir="${build.classes}"
 *            debug="${debug}"
 *            optimize="${optimize}"
 *            deprecation="${deprecation}"
 *            target="${target.vm}"
 *            encoding="iso-8859-1">
 *           <classpath refid="myclasspath"/>
 *       &lt;/opp>
 *     &lt;/target>
 * </code></pre>
 *
 * @author James Davidson <a href="/ozone-dev/04-2001/mailto:duncan@x180.com">duncan@x180.com</a>
 * @author Robin Green <a href="/ozone-dev/04-2001/mailto:greenrd@hotmail.com">greenrd@hotmail.com</a>
 * @author <a href="/ozone-dev/04-2001/mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
 * @author <a href="/ozone-dev/04-2001/mailto:jayglanville@home.com">J D Glanville</a>
 * @author <a href="/ozone-dev/04-2001/http://www.softwarebuero.de/">SMB</a> (Ozone Proxy Generator)
 * @author <a href="/ozone-dev/04-2001/mailto:joerg@7val.com">Jörg Prante</a> (Modifications for Ant Ozone Proxy Generator)
 *
 * @version $Id: $
 */

public class AntProxyGenerator extends Javac {

    // ---- Ozone fields

    public final static String PROXYNAME_POSTFIX = "_Proxy";

    public final static String IMPLNAME_POSTFIX = "_Impl";

    public final static String SIGNATURE_DELIMITER = "|";

    /**
     * Update methods can marked with a
     * <pre>
     *   /*update * /
     * </pre>
     * in the lines following the method signature.
     */
    public final static String UPDATE_SIGN =
        "/[*]+ *update *[*]/|// *update";

    /**
     * All method signatures in java interfaces must look like:
     * <pre>
     *   public package.return.Class[] methodname (
     * </pre>
     * Otherwise OPP is unable to find them.
     */
    public final static String METHOD_PATTERN =
        "^[ \\t]*public[ \\t]+[_a-zA-Z][_a-zA-Z0-9\\.\\[\\]]*[ \\t]+([_a-zA-Z][\\w]*)[ \\t]*\\(";

    /**
     * Update methods can marked with a
     * <pre>
     *   @update
     * </pre>
     * in its appropriate Javadoc Comment.
     */
    public final static String JAVADOC_PATTERN =
        "^[ \\t]+\\*[ \\t]*@update";

    final static String methodPattern = ".*_update";
    boolean cache = true;

    PrintWriter out;
    Class cl;
    Method[] methods;
    String proxyFileName;
    String proxyClassName;
    Object re;

    /**
     * Names of update methods as Strings.
     */
    Hashtable updateMethods;

    /**
     * Complete signature of methods already proceeded .
     */
    Hashtable doneMethods = new Hashtable();

    // --- Ant fields
    Path src;
    File srcDir;
    File destDir;
    Path compileClasspath;
    String encoding;
    boolean debug = false;
    boolean optimize = false;
    boolean deprecation = false;
    boolean depend = false;
    boolean verbose = false;
    String target;
    Path bootclasspath;
    Path extdirs;
    boolean includeAntRuntime = true;
    boolean includeJavaRuntime = false;

    boolean failOnError = true;
    File[] compileList = new File[0];

    static final String FAIL_MSG = "Proxy generator failed, messages should have been provided.";

    String packagename;

    /**
     * Ant init
     */
    public AntProxyGenerator() {
    }


   /**
     * Create a nested <src ...> element for multiple source path
     * support.
     *
     * @return a nexted src element.
     */
    public Path createSrc() {
        if (src == null) {
            src = new Path(project);
        }
        return src.createPath();
    }

    /**
     * Set the source dirs to find the source Java files.
     */
    public void setSrcdir(Path srcDir) {
        if (src == null) {
            src = srcDir;
        } else {
            src.append(srcDir);
        }
    }

    /** Gets the source dirs to find the source java files. */
    public Path getSrcdir() {
        return src;
    }

    /**
     * Set the destination directory into which the Java source
     * files should be compiled.
     */
    public void setDestdir(File destDir) {
        this.destDir = destDir;
    }

    /**
     * Gets the destination directory into which the java source files
     * should be compiled.
     */
    public File getDestdir() {
        return destDir;
    }

    /**
     * Set the classpath to be used for this compilation.
     */
    public void setClasspath(Path classpath) {
        if (compileClasspath == null) {
            compileClasspath = classpath;
        } else {
            compileClasspath.append(classpath);
        }
    }

    /** Gets the classpath to be used for this compilation. */
    public Path getClasspath() {
        return compileClasspath;
    }

    /**
     * Maybe creates a nested classpath element.
     */
    public Path createClasspath() {
        if (compileClasspath == null) {
            compileClasspath = new Path(project);
        }
        return compileClasspath.createPath();
    }

    /**
     * Adds a reference to a CLASSPATH defined elsewhere.
     */
    public void setClasspathRef(Reference r) {
        createClasspath().setRefid(r);
    }

    /**
     * Sets the bootclasspath that will be used to compile the classes
     * against.
     */
    public void setBootclasspath(Path bootclasspath) {
        if (this.bootclasspath == null) {
            this.bootclasspath = bootclasspath;
        } else {
            this.bootclasspath.append(bootclasspath);
        }
    }

    /**
     * Gets the bootclasspath that will be used to compile the classes
     * against.
     */
    public Path getBootclasspath() {
        return bootclasspath;
    }

    /**
     * Maybe creates a nested classpath element.
     */
    public Path createBootclasspath() {
        if (bootclasspath == null) {
            bootclasspath = new Path(project);
        }
        return bootclasspath.createPath();
    }

    /**
     * Adds a reference to a CLASSPATH defined elsewhere.
     */
    public void setBootClasspathRef(Reference r) {
        createBootclasspath().setRefid(r);
    }

    /**
     * Sets the extension directories that will be used during the
     * compilation.
     */
    public void setExtdirs(Path extdirs) {
        if (this.extdirs == null) {
            this.extdirs = extdirs;
        } else {
            this.extdirs.append(extdirs);
        }
    }

    /**
     * Gets the extension directories that will be used during the
     * compilation.
     */
    public Path getExtdirs() {
        return extdirs;
    }

    /**
     * Maybe creates a nested classpath element.
     */
    public Path createExtdirs() {
        if (extdirs == null) {
            extdirs = new Path(project);
        }
        return extdirs.createPath();
    }

    /**
     * Throw a BuildException if compilation fails
     */
    public void setFailonerror(boolean fail) {
        failOnError = fail;
    }

    /**
     * Proceed if compilation fails
     */
    public void setProceed(boolean proceed) {
        failOnError = !proceed;
    }

    /**
     * Gets the failonerror flag.
     */
    public boolean getFailonerror() {
        return failOnError;
    }

    /**
     * Set the deprecation flag.
     */
    public void setDeprecation(boolean deprecation) {
        this.deprecation = deprecation;
    }

    /** Gets the deprecation flag. */
    public boolean getDeprecation() {
        return deprecation;
    }

    /**
     * Set the Java source file encoding name.
     */
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    /** Gets the java source file encoding name. */
    public String getEncoding() {
        return encoding;
    }

    /**
     * Set the debug flag.
     */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    /** Gets the debug flag. */
    public boolean getDebug() {
        return debug;
    }

    /**
     * Set the optimize flag.
     */
    public void setOptimize(boolean optimize) {
        this.optimize = optimize;
    }

    /** Gets the optimize flag. */
    public boolean getOptimize() {
        return optimize;
    }

    /**
     * Set the depend flag.
     */
    public void setDepend(boolean depend) {
        this.depend = depend;
    }

    /** Gets the depend flag. */
    public boolean getDepend() {
        return depend;
    }

    /**
     * Set the verbose flag.
     */
    public void setVerbose(boolean verbose) {
        this.verbose = verbose;
    }

    /** Gets the verbose flag. */
    public boolean getVerbose() {
        return verbose;
    }

    /**
     * Sets the target VM that the classes will be compiled for. Valid
     * strings are "1.1", "1.2", and "1.3".
     */
    public void setTarget(String target) {
        this.target = target;
    }

    /** Gets the target VM that the classes will be compiled for. */
    public String getTarget() {
        return target;
    }

    /**
     * Include ant's own classpath in this task's classpath?
     */
    public void setIncludeantruntime( boolean include ) {
        includeAntRuntime = include;
    }

    /**
     * Gets whether or not the ant classpath is to be included in the
     * task's classpath.
     */
    public boolean getIncludeantruntime() {
        return includeAntRuntime;
    }

    /**
     * Sets whether or not to include the java runtime libraries to this
     * task's classpath.
     */
    public void setIncludejavaruntime( boolean include ) {
        includeJavaRuntime = include;
    }

    /**
     * Gets whether or not the java runtime should be included in this
     * task's classpath.
     */
    public boolean getIncludejavaruntime() {
        return includeJavaRuntime;
    }

    /**
     * Sets the OPP package prefix
     */
    public void setPackage(String packagename) {
        this.packagename = packagename;
    }

    /**
     * Gets the OPP package prefix
     */
    public String getPackage() {
        return packagename;
    }

    /**
     *  Ant task execution
     */
    public void execute() throws BuildException {
        // first off, make sure that we've got a srcdir

        if (src == null) {
            throw new BuildException("srcdir attribute must be set!", location);
        }
        String [] list = src.list();
        if (list.length == 0) {
            throw new BuildException("srcdir attribute must be set!", location);
        }

        if (destDir != null && !destDir.isDirectory()) {
            throw new BuildException("destination directory \"" + destDir +
                "\" does not exist or is not a directory", location);
        }
        // scan source directories and dest directory to build up
        // compile lists
        resetFileLists();
        srcDir = null;
        for (int i=0; i<list.length; i++) {
            srcDir = (File)project.resolveFile(list[i]);
            if (!srcDir.exists()) {
                throw new BuildException("srcdir \"" + srcDir.getPath() +
                    "\" does not exist!", location);
            }
            DirectoryScanner ds = this.getDirectoryScanner(srcDir);
            String[] files = ds.getIncludedFiles();
            scanDir(srcDir, destDir != null ? destDir : srcDir, files);
        }

        // compile the 'Impl.java' source files

        String compiler = project.getProperty("build.compiler");
        if (compiler == null) {
            if (Project.getJavaVersion().startsWith("1.3")) {
                compiler = "modern";
            } else {
                compiler = "classic";
            }
        }

        if (compileList.length > 0) {
            CompilerAdapter adapter = CompilerAdapterFactory.getCompiler(
              compiler, this );
            log("Compiling " + compileList.length +
                " source file"
                + (compileList.length == 1 ? "" : "s")
                + (destDir != null ? " to " + destDir : ""));

            // now we need to populate the compiler adapter
            adapter.setJavac( this );

            // lets execute the compiler!!
            if (!adapter.execute()) {
                if (failOnError) {
                    throw new BuildException(FAIL_MSG, location);
                }
                else {
                    log(FAIL_MSG, Project.MSG_ERR);
                }
            }
        }

        // Generate proxy source
        if (packagename == null) {
            throw new BuildException("package attribute must be set!", location);
        }
        for (int i = 0; i < compileList.length; i++) {
            try {
                String name = compileList[i].getName();
                String classname = packagename + "." + name.substring(0, name.length() - ".java".length() );
                log("Looking for class " + classname );
                updateMethods = new Hashtable();
                doneMethods = new Hashtable();
                Class cl = Class.forName( classname );
                initProxy( cl, methodPattern );
                log( "Searching update methods" );
                searchUpdateMethods( cl );
                log( "Generating source" );
                generateSource();
            } catch (Exception e) {
                throw new BuildException("cannot generate proxy source for class " +
                    compileList[i].getName() + ", reason: ", e );
            }
        }

        // Compile proxies

        // scan source directories and dest directory to build up
        // compile lists
        resetFileLists();
        for (int i=0; i<list.length; i++) {
            srcDir = (File)project.resolveFile(list[i]);
            if (!srcDir.exists()) {
                throw new BuildException("srcdir \"" + srcDir.getPath() +
                    "\" does not exist!", location);
            }
            DirectoryScanner ds = this.getDirectoryScanner(srcDir);
            String[] files = ds.getIncludedFiles();
            scanProxyDir(srcDir, destDir != null ? destDir : srcDir, files);
        }

        // compile the 'Impl_Proxy.java' source files

        compiler = project.getProperty("build.compiler");
        if (compiler == null) {
            if (Project.getJavaVersion().startsWith("1.3")) {
                compiler = "modern";
            } else {
                compiler = "classic";
            }
        }

        if (compileList.length > 0) {
            CompilerAdapter adapter = CompilerAdapterFactory.getCompiler(
              compiler, this );
            log("Compiling " + compileList.length +
                " proxy source file"
                + (compileList.length == 1 ? "" : "s")
                + (destDir != null ? " to " + destDir : ""));

            // now we need to populate the compiler adapter
            adapter.setJavac( this );

            // lets execute the compiler!!
            if (!adapter.execute()) {
                if (failOnError) {
                    throw new BuildException(FAIL_MSG, location);
                }
                else {
                    log(FAIL_MSG, Project.MSG_ERR);
                }
            }
        }
    }

    /**
     * Clear the list of files to be compiled and copied..
     */
    protected void resetFileLists() {
        compileList = new File[0];
    }

    /**
     * Scans the directory looking for source files to be compiled.
     * The results are returned in the class variable compileList
     */
    protected void scanDir(File srcDir, File destDir, String files[]) {
        GlobPatternMapper m = new GlobPatternMapper();
        m.setFrom("*Impl.java");
        m.setTo("*Impl.class");
        SourceFileScanner sfs = new SourceFileScanner(this);
        File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);

        if (newFiles.length > 0) {
            File[] newCompileList = new File[compileList.length +
                newFiles.length];
            System.arraycopy(compileList, 0, newCompileList, 0,
                    compileList.length);
            System.arraycopy(newFiles, 0, newCompileList,
                    compileList.length, newFiles.length);
            compileList = newCompileList;
        }
    }

    /**
     * Scans the directory looking for proxies to be compiled.
     * The results are returned in the class variable compileList
     */
    protected void scanProxyDir(File srcDir, File destDir, String files[]) {
        GlobPatternMapper m = new GlobPatternMapper();
        m.setFrom("*Impl_Proxy.java");
        m.setTo("*Impl_Proxy.class");
        SourceFileScanner sfs = new SourceFileScanner(this);
        File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);

        if (newFiles.length > 0) {
            File[] newCompileList = new File[compileList.length +
                newFiles.length];
            System.arraycopy(compileList, 0, newCompileList, 0,
                    compileList.length);
            System.arraycopy(newFiles, 0, newCompileList,
                    compileList.length, newFiles.length);
            compileList = newCompileList;
        }
    }

    /** Gets the list of files to be compiled. */
    public File[] getFileList() {
        return compileList;
    }



    public void initProxy( Class clazz, String _methodPattern) throws Exception
    {
        cl = clazz;
        methods = methodsOfClass( clazz );
        re = newRE( _methodPattern, false );
        proxyFileName = srcDir + File.separator + rawClassName( clazz ) + PROXYNAME_POSTFIX + ".java";
        proxyClassName = clazz.getName() + PROXYNAME_POSTFIX;

        log( "Creating " + proxyFileName );

        if (!(clazz instanceof Serializable)) {
            log( clazz.getName() + " does not implement Serializable interface." );
            return;
        }

        if (!OzoneRemote.class.isAssignableFrom( clazz )) {
            throw new BuildException( clazz.getName() + " does not implement OzoneRemote interface." );
        }
    }

    public void generateSource() {
        try {
            out = new PrintWriter( new FileOutputStream( proxyFileName ) );
            makeHeader();
            makeCtors();
            makeMethods();
            checkMethodLists();
            out.print( "   }\n" );
        } catch (Exception e) {
            log( rawClassName( cl ) + ".java:0: " + e.getMessage() );
            return;
        } finally {
            out.close();
        }
    }

    
    public void makeHeader() throws Exception {
        out.print( "// Proxy class generated by Ant Proxy Generator for OzoneDB .\n" );
        out.print( "// DO NOT EDIT!\n" );
        out.print( "\n" );
        if (!packageName( cl ).equals( "" )) {
            out.print( "package " + packageName( cl ) + ";\n" );
            out.print( "\n" );
        } 
        out.print( "import org.ozoneDB.*;\n" );
        out.print( "import org.ozoneDB.core.ObjectID;\n" );
        out.print( "import org.ozoneDB.core.Lock;\n" );
        out.print( "import org.ozoneDB.core.ResultConverter;\n" );
        
        out.print( "\n" );
        out.print( "/**\n" );
        out.print( " * This class was automatically generated by Ant Proxy Generator for OzoneDB.\n" );
        out.print( " * Do not instantiate or use this class directly.\n" );
        out.print( " */\n" );
        out.print( "public final class " + rawClassName( proxyClassName ) + " \n" );
        out.print( "       extends OzoneProxy\n" );
        
        Class[] ifs = cl.getInterfaces();
        for (int i = 0, c = 0; i < ifs.length; i++) {
            // filter methods that are not inherited from a OzoneRemote
            // interface
            if (OzoneRemote.class.isAssignableFrom( ifs[i] )) {
                if (c++ == 0) {
                    out.print( "       implements " );
                } else {
                    out.print( ", " );
                }
                
                out.print( ifs[i].getName() );
            }
        } 
        out.print( " {\n" );
        out.print( "\n   static final long serialVersionUID = 1L;\n" );
    }


    
    public void makeCtors() throws Exception {
        out.print( "\n" );
        out.print( "   public " + rawClassName( proxyClassName ) + "() {\n" );
        out.print( "      super();\n" );
        out.print( "      }\n" );
        out.print( "\n\n" );
        
        // create default ctor that is responsible to create a pure proxy
        // without interacting with any database
        out.print( "   public " + rawClassName( proxyClassName ) 
                + " (ObjectID oid, OzoneInterface link) {\n" );
        out.print( "      super (oid, link);\n" );
        out.print( "      }\n" );

        java.lang.reflect.Constructor[] ctors = cl.getDeclaredConstructors();
        for (int i = 0; i < ctors.length; i++) {
            makeCtor( ctors[i] );
        } 
    }
    
    
    public void makeCtor( Constructor ctor ) throws Exception {
        // string buffer to build the signatur of this method
        StringBuffer signaturBuf = new StringBuffer( "" );
        signaturBuf.append( "   public " + rawClassName( proxyClassName ) + " (" );
        Class[] args = ctor.getParameterTypes();
        // skip default ctor
        if (args.length == 0) {
            return;
        }
        for (int i = 0; i < args.length; i++) {
            if (i != 0) {
                signaturBuf.append( ", " );
            } 
            signaturBuf.append( typecodeForClass( args[i] ) + " arg" + i );
        } 
        signaturBuf.append( ")" );
        
        out.print( "\n\n" );
        out.print( signaturBuf.toString() );

        Class[] excs = ctor.getExceptionTypes();
        for (int i = 0; i < excs.length; i++) {
            out.print( i == 0 ? " throws " : ", " );
            out.print( excs[i].getName() );
        }
        out.print( " {\n" );

        out.print( "      try {\n" );
        out.print( "         link = ExternalDatabase.forThread (Thread.currentThread());\n" );
        out.print( "         if (link == null)\n" );
        out.print( "            throw new TransactionExc (\"Thread has not yet joined a transaction.\");\n" );
        out.print( "         \n" );
        
        //array of arguments
        out.print( "         Object[] args = {" );
        for (int i = 0; i < args.length; i++) {
            out.print( i > 0 ? ", " : "" );
            if (args[i].isPrimitive()) {
                out.print( "new " + wrappercodeForPrimitive( args[i] ) + "(arg" + i + ")" );
            } else {
                out.print( "arg" + i );
            } 
        }
        out.print( "};\n" );
        
        String sig = signature( ctor.getParameterTypes() );
        out.print( "         OzoneProxy proxy = link.createObject (\"" + cl.getName()
                + "_Impl\", OzoneInterface.Public, null, " + sig + ", args);\n" );
        out.print( "         remoteID = proxy.remoteID();\n" );
        out.print( "         }\n" );
        
        //exceptions
        boolean alreadyCatched = false;
        for (int i = 0; i < excs.length; i++) {
            out.print( "      catch (" + excs[i].getName() + " e) {\n" );
            out.print( "         e.fillInStackTrace();\n" );
            out.print( "         throw e; }\n" );
            // out.print ("         throw (" + excs[i].getName() + ")e.fillInStackTrace(); }\n");
            if (excs[i].getName().equals( "java.lang.Exception" )) {
                alreadyCatched = true;
            } 
        } 
        if (!alreadyCatched) {
            out.print( "      catch (Exception e) {\n" );
            out.print( "         e.fillInStackTrace();\n" );
            out.print( "         throw new UnexpectedException (e.toString()); }\n" );
        }
        out.print( "      }\n" );
    } 
    

    public void makeMethods() throws Exception {
        Class[] ifs = cl.getInterfaces();
        for (int i = 0; i < ifs.length; i++) {
            // filter interfaces that are not inherited from a OzoneRemote
            // interface
            if (OzoneRemote.class.isAssignableFrom( ifs[i] )) {
                Method[] methods = ifs[i].getMethods();
                for (int j = 0; j < methods.length; j++) {
                    makeMethod( methods[j] );
                } 
            } 
        } 
    }

    
    /**
     * Checks if all method names in the update lists 
     * are processed.
     */
    public void checkMethodLists() throws Exception {

        for (Enumeration e = updateMethods.keys(); e.hasMoreElements(); ) {
            Object o = e.nextElement();
            if (!doneMethods.containsKey(o)) {
                log( rawClassName( cl ) +
                    ": method with name: '" + o.toString() +
                    "' could not be processed, but is marked as 'update' or 'WRITE'-LOCK");
            }
        }
    }

    
    public void makeMethod( Method m ) throws Exception {
        
        // string buffer to build the signatur of this method
        StringBuffer signaturBuf = new StringBuffer( "" );
        
        signaturBuf.append( "   public " + typecodeForClass( m.getReturnType() ) + " " + m.getName() + " (" );
        
        //argumente in declaration schreiben
        Class[] args = m.getParameterTypes();
        for (int i = 0; i < args.length; i++) {
            if (i != 0) {
                signaturBuf.append( ", " );
            } 
            signaturBuf.append( typecodeForClass( args[i] ) + " arg" + i );
        } 
        signaturBuf.append( ")" );

        String signaturStr = signaturBuf.toString();
        //The getMethods() method returns methods twice, if they are
        //declared in different interfaces. So we have to check if this
        //signatur was already proceeded.
        if (doneMethods.contains( signaturStr )) {
            return;
        } 
        
        out.print( "\n\n" );
        out.print( signaturStr );

        Class[] excs = m.getExceptionTypes();
        for (int i = 0; i < excs.length; i++) {
            out.print( i == 0 ? " throws " : ", " );
            out.print( excs[i].getName() );
        }
        out.print( " {\n" );
        
        //implementation
        out.print( "      try {\n" );

        boolean update = reMatch( re, m.getName() ) || updateMethods.contains( m.getName() );
        String sig = signature( m.getParameterTypes() );
        if (update) {
            log( "      " + m.getName() + " (" + sig + ")" );
        } 
        
        // code to directly invoke the target method
        if (cache) {
            out.print( "         Object target = link.fetch (this, " + (update 
                    ? "Lock.LEVEL_WRITE" 
                    : "Lock.LEVEL_READ") + ");\n" );
            out.print( "         if (target != null) {\n" );
            
            // convert arguments if needed
            for (int i = 0; i < args.length; i++) {
                if (!args[i].isPrimitive()) {
                    out.print( "            arg" + i + " = (" + typecodeForClass( args[i] )
                            + ")ResultConverter.substituteOzoneCompatibles (arg" + i + ");\n" );
                } 
            } 
            
            if (!m.getReturnType().getName().equals( "void" )) {
                if (!m.getReturnType().isPrimitive()) {
                    out.print( "            return (" + typecodeForClass( m.getReturnType() ) 
                            + ")ResultConverter.substituteOzoneCompatibles (((" + cl.getName() + ")target)."
                            + m.getName() + "(" );
                } else {
                    out.print( "            return ((" + cl.getName() + ")target)." + m.getName() + "(" );
                } 
            } else {
                out.print( "            ((" + cl.getName() + ")target)." + m.getName() + "(" );
            }
            
            for (int i = 0; i < args.length; i++) {
                out.print( i > 0 ? ", " : "" );
                out.print( "arg" + i );
            }
            if (!m.getReturnType().getName().equals( "void" )) {
                if (!m.getReturnType().isPrimitive()) {
                    out.print( ")" );
                } 
            } 
            out.print( ");\n" );
            out.print( "            }\n" );
            out.print( "         else {\n" );
        } 
        
        //array of arguments
        out.print( "            Object[] args = {" );
        for (int i = 0; i < args.length; i++) {
            out.print( i > 0 ? ", " : "" );
            if (args[i].isPrimitive()) {
                out.print( "new " + wrappercodeForPrimitive( args[i] ) + "(arg" + i + ")" );
            } else {
                out.print( "arg" + i );
            }
        }
        out.print( "};\n" );

        out.print( "            Object result = link.invoke (this, " + methodArrayIndex( methods,
                m ) + ", args, " + (update
                ? "Lock.LEVEL_WRITE"
                : "Lock.LEVEL_READ") + ");\n" );
        
        // return value
        if (!m.getReturnType().getName().equals( "void" )) {
            if (m.getReturnType().isPrimitive()) {
                out.print( "            return " + returncodeForPrimitive( m.getReturnType(),
                        "result" ) + ";\n" );
            } else {
                out.print( "            return (" + typecodeForClass( m.getReturnType() ) + ")result;\n" );
            }
        }

        if (cache) {
            out.print( "            }\n" );
        } 
        
        out.print( "         }\n" );

        // user defined exceptions
        boolean excAlreadyCatched = false;
        boolean rtAlreadyCatched = false;
        for (int i = 0; i < excs.length; i++) {
            out.print( "      catch (" + excs[i].getName() + " e) {\n" );
            out.print( "         e.fillInStackTrace();\n" );
            out.print( "         throw e;\n" );
            out.print( "         }\n" );
            // out.print ("         throw (" + excs[i].getName() + ")e.fillInStackTrace(); }\n");

            if (excs[i].getName().equals( "java.lang.Exception" )) {
                excAlreadyCatched = true;
            }
            if (excs[i].getName().equals( "java.lang.RuntimeException" )) {
                rtAlreadyCatched = true;
            }
        }

        // runtime exceptions
        if (!rtAlreadyCatched && !excAlreadyCatched) {
            out.print( "      catch (RuntimeException e) {\n" );
            out.print( "         e.fillInStackTrace();\n" );
            out.print( "         throw e;\n" );
            out.print( "         }\n" );
        }
        
        // all exceptions left
        if (!excAlreadyCatched) {
            out.print( "      catch (Exception e) {\n" );
            out.print( "         e.fillInStackTrace();\n" );
            out.print( "         throw new UnexpectedException (e.toString());\n" );
            out.print( "         }\n" );
        }

        out.print( "      }\n" );

        // save proceeded methods
        doneMethods.put( m.getName(), signaturStr );
    }


    /**
     * Gives the Java source code for the type of the specified class.
     */
    protected String typecodeForClass( Class cl ) throws Exception {
        String name = cl.getName();
        String ret = "";

        // array
        if (cl.isArray()) {
            while (cl.isArray()) {
                ret = ret + "[]";
                cl = cl.getComponentType();
            }
            ret = cl.getName() + ret;
        } else {
            // normal reference and primitive types
            ret = name;
        }
        return ret;
    }


   /**
     * Search all remote interfaces (implement OzoneRemote) for update methods.
     */
    public void searchUpdateMethods(Class cl) throws Exception {
        Class dbRemote = Class.forName( "org.ozoneDB.OzoneRemote" );
        Class[] ifs = cl.getInterfaces();
        for (int i = 0; i < ifs.length; i++) {
            if (dbRemote.isAssignableFrom( ifs[i] ) && !dbRemote.equals( ifs[i] )) {
                searchUpdateMethods( ifs[i], updateMethods, doneMethods );
            }
        }
    }

    /**
     * Search all methods that are marked in the source code of the remote
     * Java interface.
     */
    public void searchUpdateMethods( Class dbInterface, Hashtable updateMethods,
        Hashtable doneMethods ) throws Exception {

        // method name regexp
        Object re = newRE( UPDATE_SIGN, true );
        // method comment regexp
        Object mre = newRE( METHOD_PATTERN, false );
        // javadoc regexp
        Object jre = newRE( JAVADOC_PATTERN, false );
        // javadoc comemnt start
        Object docstart = newRE( "/\\*\\*", false );

        //search *.java source in source directory
        String sourceName = srcDir.getPath() + File.separator + rawClassName( dbInterface ) + ".java";
        log("Looking for " + sourceName);
        if (!new File( sourceName ).exists()) {
            log( "No source file for this interface found." );
            return;
        }

        LineNumberReader in = new LineNumberReader( new FileReader( sourceName ) );
        String line = in.readLine();
        // to support multi line method signatures
        // the regexp for update must match on the method signature line
        //      or one the followings
        boolean isJavadocUpdate = false;
        String lastMatchedMethod = null;
        while (line != null) {
            boolean isUpdate = reSearch( re, line, 0, 0 ) != null;
            // before each method stands his appropriate javadoc comment
            String match = reSearch( docstart, line, 0, 0 );
            if (match != null) {
                lastMatchedMethod = null;
                isJavadocUpdate = false;
            }
            match = reSearch( mre, line, 0, 1 );
            if (match != null) {
                lastMatchedMethod = match;
            }
            match = reSearch( jre, line, 0, 0 );
            if (match != null) {
                isJavadocUpdate = true;
            }
            if (lastMatchedMethod != null && (isUpdate || isJavadocUpdate)) {
                String methodName = lastMatchedMethod;
                updateMethods.put( methodName, methodName );
                lastMatchedMethod = null;
                isJavadocUpdate = false;
            } else if (lastMatchedMethod == null && isUpdate) {
                log( rawClassName( dbInterface ) + ".java:" + Integer.toString(in.getLineNumber()) +
                        ": unable to determine the method name for the "
                        + " found update definition in line " + in.getLineNumber() + "." );
            } else {
                if (lastMatchedMethod != null && !isUpdate && !isJavadocUpdate) {
                    String methodName = lastMatchedMethod;
                    if (updateMethods.contains( methodName )) {
                        log( rawClassName( dbInterface ) + ".java:" + Integer.toString(in.getLineNumber()) +
                                ": all '" + methodName + "' methods will be marked as update methods." );
                    }
                }
            }
            line = in.readLine();
        }
        in.close();
    }

    // --- from OPPHelper

    public Object newRE( String s, boolean ignoreCase ) {
        int flags = ignoreCase ? RE.MATCH_CASEINDEPENDENT : RE.MATCH_NORMAL;
        return new RE( s, flags );
    }

    public static boolean reMatch( Object re, String s ) {
        return ((RE)re).match( s );
    }

    public String reSearch( Object re, String s, int start, int paren ) {
        for (int i = start; i < s.length(); i++) {
            if (((RE)re).match( s, i )) {
                return ((RE)re).getParen( paren );
            }
        }
        return null;
    }

    public String rawClassName( Class c ) {
        return rawClassName( c.getName() );
    }


    public String rawClassName( String name ) {
        int index = name.lastIndexOf( '.' );
        return index != -1 ? name.substring( index + 1 ) : name;
    }

    public Method[] methodsOfClass( Class cl ) {
        Method[] methods = cl.getMethods();
        TreeSet set = new TreeSet();
        for (int i = 0; i < methods.length; i++) {
            String name = methods[i].getName();
            String sig = signature( methods[i].getParameterTypes() );
            MethodKey key = new MethodKey( cl.getName(), name, sig, methods[i] );
            set.add( key );
        }
        int i = 0;
        for (Iterator it = set.iterator(); it.hasNext(); ) {
            MethodKey key = (MethodKey)it.next();
            methods[i++] = key.method();
        }
        return methods;
    }

    public String signature( Class[] args ) {
        String result = new String();
        for (int i = 0; i < args.length; i++) {
            result = i > 0 ? result + SIGNATURE_DELIMITER : result;
            result = result + args[i].getName();
        }
        return "\"" + result + "\"";
    }

    public String packageName( Class c ) {
        String name = c.getName();
        int index = name.lastIndexOf( '.' );
        return index != -1 ? name.substring( 0, index ) : "";
    }

    public String wrappercodeForPrimitive( Class cl ) {
        String ret;
        String name = cl.getName();
        if (name.equals( "int" )) {
            ret = "Integer";
        } else if (name.equals( "char" )) {
            ret = "Character";
        } else {
            ret = name.substring( 0, 1 ).toUpperCase() + name.substring( 1 );
        }
        return ret;
    }

    public String returncodeForPrimitive( Class cl, String varName ) throws BuildException {
        String ret;
        String name = cl.getName();
        if (name.equals( "int" )) {
            ret = "((Integer)" + varName + ").intValue()";
        } else if (name.equals( "boolean" )) {
            ret = "((Boolean)" + varName + ").booleanValue()";
        } else if (name.equals( "char" )) {
            ret = "((Character)" + varName + ").charValue()";
        } else if (name.equals( "long" )) {
            ret = "((Long)" + varName + ").longValue()";
        } else if (name.equals( "float" )) {
            ret = "((Float)" + varName + ").floatValue()";
        } else if (name.equals( "double" )) {
            ret = "((Double)" + varName + ").doubleValue()";
        } else if (name.equals( "byte" )) {
            ret = "((Byte)" + varName + ").byteValue()";
        } else if (name.equals( "short" )) {
            ret = "((Short)" + varName + ").shortValue()";
        } else {
            throw new BuildException( "unknown type: '" + name + "'" );
        }
        return ret;
    }

    /**
     * Returns the array index of the specified method within the array
     * of methods of this class.
     */
    public int methodArrayIndex( Method[] methods, Method m ) {
        String mName = m.getName();
        String mSig = signature( m.getParameterTypes() );

        for (int i = 0; i < methods.length; i++) {
            String cName = methods[i].getName();
            String cSig = signature( methods[i].getParameterTypes() );
            if (mName.equals( cName ) && mSig.equals( cSig )) {
                return i;
            }
        }
        throw new BuildException( m + ": unable to find method in class." );
    }

    /**
     *  Method Key from Ozone core
     */
    class MethodKey implements Comparable {

        String methodName;
        String sig;
        String className;
        Method method;

        public MethodKey( String className, String methodName, String sig, Method method ) {
            this.className = className;
            this.methodName = methodName;
            this.sig = sig;
            this.method = method;
        }

        public Method method() {
            return method;
        }

        public int hashCode() {
            return methodName.hashCode() ^ sig.hashCode();
        }

        public boolean equals( Object obj ) {
            MethodKey rhs = (MethodKey)obj;
            return className.equals( rhs.className ) &&
               sig.equals( rhs.sig ) &&
               methodName.equals( rhs.methodName );
        }

        public int compareTo(Object obj) {
            MethodKey rhs = (MethodKey)obj;
            StringBuffer buf = new StringBuffer( className );
            buf.append( methodName );
            buf.append( sig );
            StringBuffer rhsBuf = new StringBuffer( rhs.className );
            rhsBuf.append( rhs.methodName );
            rhsBuf.append( rhs.sig );
            return buf.toString().compareTo( rhsBuf.toString() );
        }
    }

}