--- CopyEjbcHelper.java	Mon Aug 14 11:14:05 2000
+++ EjbcHelper.java	Mon Sep 04 11:07:44 2000
@@ -54,12 +54,13 @@
 package org.apache.tools.ant.taskdefs.optional.ejb;
 
 import java.io.*;
-
+import java.util.*;
 import javax.ejb.deployment.EntityDescriptor;
 import javax.ejb.deployment.DeploymentDescriptor;
 
 import org.apache.tools.ant.*;
 import org.apache.tools.ant.types.Path;
+import java.lang.reflect.*;
 
 /**
  * A helper class which performs the actual work of the ejbc task.
@@ -282,7 +283,16 @@
             if (descriptorFile.lastModified() > classModificationTime ||
                     homeInterfaceSource.lastModified() > classModificationTime ||
                     remoteInterfaceSource.lastModified() > classModificationTime) {
-                return true;
+                // So the home or remote interface have been recompiled. Now it is time
+                // to see whether this was necessary. Check the methods of home/remote
+                // against the container classes that implement these interfaces,
+                // if they don't match then regenerate.
+
+                  return !(matchInterfaceAndImpl(dd.getHomeInterfaceClassName(),
+                                                 dd.getEnterpriseBeanClassName() + "HomeImpl")
+                          &&
+                           matchInterfaceAndImpl(dd.getRemoteInterfaceClassName(),
+                                                 dd.getEnterpriseBeanClassName() + "EOImpl"));
             }
             
             if (primaryKeyClassSource != null && 
@@ -303,6 +313,119 @@
         }
         
         return false;
+    }
+
+    /**
+     * Compare the methods as defined in an interface class against
+     * the implementation in a container class.
+     *
+     * @return boolean indicating whether the container class implements exactly
+     *                 the classes in the ejb interface, ignoring the generated
+     *                 default methods (eg. setup(), setEJBHome() etc)
+     */
+    private boolean matchInterfaceAndImpl(String interfaceName, String implName) {
+        boolean theyMatch = true;
+        try {
+          // get the interface and implementation Class objects
+          Class interfaceClass = Class.forName(interfaceName);
+          Class implClass = Class.forName(implName);
+
+          // get the methods that are declared in these classes
+          Method[] icMethods = interfaceClass.getDeclaredMethods();
+          Method[] implcMethods = implClass.getDeclaredMethods();
+
+          // in order to make the comparison easier, the toString() method of Method
+          // is used. It generates a standardized string with all the modifiers, arguments
+          // and exceptions
+          Vector icMethodNames = new Vector();
+          Vector implcMethodNames = new Vector();
+          StringBuffer sb; String s;
+          // fill two vectors with these method names
+          for (int i=0; i<icMethods.length; i++) {
+            icMethodNames.addElement(normalizeMethodeSignature(interfaceName, icMethods[i].toString()));
+          }
+          for (int i=0; i<implcMethods.length; i++) {
+            implcMethodNames.addElement(normalizeMethodeSignature(implName, implcMethods[i].toString()));
+          }
+          // now remove the methods that are in the interface from the vector with
+          // those of the implementation class
+          for (int i=0; i<icMethodNames.size() && theyMatch ; i++) {
+            theyMatch = implcMethodNames.removeElement(icMethodNames.elementAt(i));
+          }
+          // 'theyMatch' indicates that all methods were found, now continue by checking
+          // that there are only standard container methods left
+          if (theyMatch) {
+            String methodName;
+            for (int i=0; i<implcMethodNames.size() && theyMatch; i++) {
+              methodName = (String)implcMethodNames.elementAt(i);
+              if (methodName.indexOf("public void create()") == -1 &&
+                  methodName.indexOf("public void remove()") == -1 &&
+                  methodName.indexOf("public void setEJBHome(") == -1 &&
+                  methodName.indexOf("public void setup()") == -1) {
+                theyMatch = false;
+              }
+            }
+          }
+
+        // Part of the work is done by the classloader, it throws an IncompatibleClassChangeError
+        // in case the implementation class doesn't implement some of the methods on the
+        // interface, this clearly indicates a no-match
+        } catch (Throwable e) {
+          theyMatch = false;
+        }
+        return theyMatch;
+    }
+
+    /**
+     * To simplify the comparison of the methods in the interface and in the
+     * container generated implementation class, the method name as returned by Method.toString()
+     * is normalized. See inline comments.
+     */
+    private String normalizeMethodeSignature(String classname, String methodSignature) {
+
+      // remove classname + '.' that lead the method name. This would obviously
+      // make the comparison fail.
+      int i = methodSignature.indexOf(classname+".");
+      StringBuffer res = new StringBuffer(methodSignature.substring(0,i));
+      res.append(methodSignature.substring(i + classname.length() + 1));
+
+      // one of the classes is an interface, so its methods are abstract
+      // this difference is anticipated and should therefore be ignored
+      String res2 = res.toString();
+      i = res2.indexOf("abstract");
+      if (i != -1) {
+        res = new StringBuffer(res2.substring(0,i));
+        res.append(res2.substring(i + 9));
+      }
+      // sort the exceptions, order is unspecified in Method.toString()
+      res2 = res.toString();
+      int throwsIndex = res2.indexOf(" throws");
+      if (throwsIndex != -1) {
+        StringTokenizer st = new StringTokenizer(res2.substring(throwsIndex+8), ",");
+        Vector sortedExcp = new Vector();
+        String excp;
+        while (st.hasMoreElements()) {
+          excp = st.nextToken();
+          boolean ready = false;
+          for (int j=0; j<sortedExcp.size() && !ready; j++) {
+            if (excp.compareTo((String)sortedExcp.elementAt(j)) < 0) {
+              sortedExcp.insertElementAt(excp,j);
+              ready = true;
+            }
+          }
+          if (!ready) {
+            sortedExcp.addElement(excp);
+          }
+        }
+        res = new StringBuffer(res2.substring(0,throwsIndex));
+        String prefix = " throws ";
+        for (int k=0; k<sortedExcp.size(); k++) {
+          res.append(prefix);
+          prefix = ",";
+          res.append(sortedExcp.elementAt(k));
+        }
+      }
+      return res.toString();
     }
 
     /**
