[jboss-svn-commits] JBL Code SVN: r13591 - in labs/jbossrules/trunk/drools-core/src: main/java/org/drools/common and 7 other directories.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Tue Jul 17 22:15:24 EDT 2007


Author: tirelli
Date: 2007-07-17 22:15:24 -0400 (Tue, 17 Jul 2007)
New Revision: 13591

Added:
   labs/jbossrules/trunk/drools-core/src/test/java/org/drools/CheeseEqual.java
Modified:
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ClassObjectType.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AbstractWorkingMemory.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/EqualityKey.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/facttemplates/FactTemplateObjectType.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/Rete.java
   labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/ObjectType.java
   labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java
   labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java
   labs/jbossrules/trunk/drools-core/src/test/java/org/drools/spi/MockObjectType.java
Log:
JBRULES-1014: fixing Shadow Proxy and working around some final classes

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ClassObjectType.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ClassObjectType.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ClassObjectType.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -16,12 +16,6 @@
  * limitations under the License.
  */
 
-import java.lang.reflect.Field;
-
-import org.drools.RuntimeDroolsException;
-import org.drools.objenesis.Objenesis;
-import org.drools.objenesis.ObjenesisStd;
-import org.drools.objenesis.instantiator.ObjectInstantiator;
 import org.drools.spi.ObjectType;
 
 /**
@@ -79,6 +73,20 @@
     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
     /**
+     * Determine if the passed <code>Class</code> matches to the object type
+     * defined by this <code>objectType</code> instance.
+     * 
+     * @param clazz
+     *            The <code>Class</code> to test.
+     * 
+     * @return <code>true</code> if the <code>Class</code> matches this
+     *         object type, else <code>false</code>.
+     */
+    public boolean matchesClass(final Class clazz) {
+        return getClassType().isAssignableFrom( clazz );
+    }
+
+    /**
      * Determine if the passed <code>Object</code> belongs to the object type
      * defined by this <code>objectType</code> instance.
      * 

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/base/ShadowProxyFactory.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -35,36 +35,35 @@
 import org.drools.asm.MethodVisitor;
 import org.drools.asm.Opcodes;
 import org.drools.asm.Type;
-import org.drools.rule.MapBackedClassLoader;
 import org.drools.util.ShadowProxyUtils;
 
 /**
  * A factory for ShadowProxy classes
  */
 public class ShadowProxyFactory {
-    private static final String UPDATE_PROXY         = "updateProxy";
-    private static final String SET_SHADOWED_OBJECT  = "setShadowedObject";
-    private static final String GET_SHADOWED_OBJECT  = "getShadowedObject";
+    private static final String           UPDATE_PROXY         = "updateProxy";
+    private static final String           SET_SHADOWED_OBJECT  = "setShadowedObject";
+    private static final String           GET_SHADOWED_OBJECT  = "getShadowedObject";
 
-    private static final String BASE_INTERFACE       = Type.getInternalName( ShadowProxy.class );
+    private static final String           BASE_INTERFACE       = Type.getInternalName( ShadowProxy.class );
 
     //private static final String FIELD_NAME_PREFIX   = "__";
 
-    public static final String  FIELD_SET_FLAG       = "IsSet";
+    public static final String            FIELD_SET_FLAG       = "IsSet";
 
-    public static final String  DELEGATE_FIELD_NAME  = "delegate";
+    public static final String            DELEGATE_FIELD_NAME  = "delegate";
 
-    public static final String  HASHCACHE_FIELD_NAME = "__hashCache";
-    
+    public static final String            HASHCACHE_FIELD_NAME = "__hashCache";
+
     private static final ProtectionDomain PROTECTION_DOMAIN;
-    
+
     static {
         PROTECTION_DOMAIN = (ProtectionDomain) AccessController.doPrivileged( new PrivilegedAction() {
             public Object run() {
                 return ShadowProxyFactory.class.getProtectionDomain();
             }
         } );
-    }      
+    }
 
     public static Class getProxy(final Class clazz) {
         try {
@@ -965,10 +964,17 @@
                              offset );
             offset += type.getSize();
         }
-        mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
-                            Type.getInternalName( clazz ),
-                            method.getName(),
-                            Type.getMethodDescriptor( method ) );
+        if ( clazz.isInterface() ) {
+            mv.visitMethodInsn( Opcodes.INVOKEINTERFACE,
+                                Type.getInternalName( clazz ),
+                                method.getName(),
+                                Type.getMethodDescriptor( method ) );
+        } else {
+            mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
+                                Type.getInternalName( clazz ),
+                                method.getName(),
+                                Type.getMethodDescriptor( method ) );
+        }
         mv.visitInsn( Type.getType( method.getReturnType() ).getOpcode( Opcodes.IRETURN ) );
         final Label l1 = new Label();
         mv.visitLabel( l1 );
@@ -1009,7 +1015,7 @@
             final Label l0 = new Label();
             mv.visitLabel( l0 );
 
-            // if ( this == object || this.delegate == object )
+            // if ( this == object || this.delegate == object || this.delegate.equals( object ) ) {
             mv.visitVarInsn( Opcodes.ALOAD,
                              0 );
             mv.visitVarInsn( Opcodes.ALOAD,
@@ -1025,17 +1031,38 @@
                                Type.getDescriptor( clazz ) );
             mv.visitVarInsn( Opcodes.ALOAD,
                              1 );
+            mv.visitJumpInsn( Opcodes.IF_ACMPEQ,
+                              l1 );
+            
+            mv.visitVarInsn(Opcodes.ALOAD, 0);
+            mv.visitFieldInsn( Opcodes.GETFIELD,
+                               className,
+                               DELEGATE_FIELD_NAME,
+                               Type.getDescriptor( clazz ) );
+            mv.visitVarInsn(Opcodes.ALOAD, 1);
+            if ( clazz.isInterface() ) {
+                mv.visitMethodInsn( Opcodes.INVOKEINTERFACE,
+                                    Type.getInternalName( clazz ),
+                                    "equals",
+                                    Type.getMethodDescriptor( Type.BOOLEAN_TYPE,
+                                                              new Type[]{Type.getType( Object.class )} ) );
+            } else {
+                mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
+                                    Type.getInternalName( clazz ),
+                                    "equals",
+                                    Type.getMethodDescriptor( Type.BOOLEAN_TYPE,
+                                                              new Type[]{Type.getType( Object.class )} ) );
+            }
             Label l2 = new Label();
-            mv.visitJumpInsn( Opcodes.IF_ACMPNE,
-                              l2 );
+            mv.visitJumpInsn(Opcodes.IFEQ, l2);
+            
+            //      return true;
             mv.visitLabel( l1 );
-
-            //      return true;
             mv.visitInsn( Opcodes.ICONST_1 );
             mv.visitInsn( Opcodes.IRETURN );
+            mv.visitLabel( l2 );
 
             // if (( object == null ) || ( ! ( object instanceof <class> ) ) ) 
-            mv.visitLabel( l2 );
             mv.visitVarInsn( Opcodes.ALOAD,
                              1 );
             final Label l3 = new Label();
@@ -1053,9 +1080,75 @@
             mv.visitLabel( l3 );
             mv.visitInsn( Opcodes.ICONST_0 );
             mv.visitInsn( Opcodes.IRETURN );
+            mv.visitLabel( l4 );
 
+            // if( object instanceof ShadowProxy && 
+            //     ( this.delegate == ((ShadowProxy)object).delegate ||
+            //       this.delegate.equals( ((ShadowProxy)object).delegate ) ) ) {
+            Label c0 = new Label();
+            mv.visitLabel( c0 );
+            mv.visitVarInsn( Opcodes.ALOAD,
+                             1 );
+            mv.visitTypeInsn( Opcodes.INSTANCEOF,
+                              className );
+            Label c1 = new Label();
+            mv.visitJumpInsn( Opcodes.IFEQ,
+                              c1 );
+            mv.visitVarInsn( Opcodes.ALOAD,
+                             0 );
+            mv.visitFieldInsn( Opcodes.GETFIELD,
+                               className,
+                               DELEGATE_FIELD_NAME,
+                               Type.getDescriptor( clazz ) );
+            mv.visitVarInsn( Opcodes.ALOAD,
+                             1 );
+            mv.visitTypeInsn( Opcodes.CHECKCAST,
+                              className );
+            mv.visitFieldInsn( Opcodes.GETFIELD,
+                               className,
+                               DELEGATE_FIELD_NAME,
+                               Type.getDescriptor( clazz ) );
+            Label c2 = new Label();
+            mv.visitJumpInsn( Opcodes.IF_ACMPEQ,
+                              c2 );
+            mv.visitVarInsn( Opcodes.ALOAD,
+                             0 );
+            mv.visitFieldInsn( Opcodes.GETFIELD,
+                               className,
+                               DELEGATE_FIELD_NAME,
+                               Type.getDescriptor( clazz ) );
+            mv.visitVarInsn( Opcodes.ALOAD,
+                             1 );
+            mv.visitTypeInsn( Opcodes.CHECKCAST,
+                              className );
+            mv.visitFieldInsn( Opcodes.GETFIELD,
+                               className,
+                               DELEGATE_FIELD_NAME,
+                               Type.getDescriptor( clazz ) );
+            if ( clazz.isInterface() ) {
+                mv.visitMethodInsn( Opcodes.INVOKEINTERFACE,
+                                    Type.getInternalName( clazz ),
+                                    "equals",
+                                    Type.getMethodDescriptor( Type.BOOLEAN_TYPE,
+                                                              new Type[]{Type.getType( Object.class )} ) );
+            } else {
+                mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
+                                    Type.getInternalName( clazz ),
+                                    "equals",
+                                    Type.getMethodDescriptor( Type.BOOLEAN_TYPE,
+                                                              new Type[]{Type.getType( Object.class )} ) );
+            }
+            mv.visitJumpInsn( Opcodes.IFEQ,
+                              c1 );
+            mv.visitLabel( c2 );
+            //     return true;
+            mv.visitInsn( Opcodes.ICONST_1 );
+            mv.visitInsn( Opcodes.IRETURN );
+            // }
+            mv.visitLabel( c1 );
+            
+
             // <class> other = (<class>) object;
-            mv.visitLabel( l4 );
             mv.visitVarInsn( Opcodes.ALOAD,
                              1 );
             mv.visitTypeInsn( Opcodes.CHECKCAST,
@@ -1322,11 +1415,19 @@
                            Type.getDescriptor( clazz ) );
         mv.visitVarInsn( Opcodes.ALOAD,
                          1 );
-        mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
-                            Type.getInternalName( clazz ),
-                            "equals",
-                            Type.getMethodDescriptor( Type.BOOLEAN_TYPE,
-                                                      new Type[]{Type.getType( Object.class )} ) );
+        if ( clazz.isInterface() ) {
+            mv.visitMethodInsn( Opcodes.INVOKEINTERFACE,
+                                Type.getInternalName( clazz ),
+                                "equals",
+                                Type.getMethodDescriptor( Type.BOOLEAN_TYPE,
+                                                          new Type[]{Type.getType( Object.class )} ) );
+        } else {
+            mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
+                                Type.getInternalName( clazz ),
+                                "equals",
+                                Type.getMethodDescriptor( Type.BOOLEAN_TYPE,
+                                                          new Type[]{Type.getType( Object.class )} ) );
+        }
         mv.visitInsn( Opcodes.IRETURN );
         Label l3 = new Label();
         mv.visitLabel( l3 );
@@ -1401,11 +1502,19 @@
                                className,
                                DELEGATE_FIELD_NAME,
                                Type.getDescriptor( clazz ) );
-            mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
-                                Type.getInternalName( clazz ),
-                                "hashCode",
-                                Type.getMethodDescriptor( Type.INT_TYPE,
-                                                          new Type[0] ) );
+            if ( clazz.isInterface() ) {
+                mv.visitMethodInsn( Opcodes.INVOKEINTERFACE,
+                                    Type.getInternalName( clazz ),
+                                    "hashCode",
+                                    Type.getMethodDescriptor( Type.INT_TYPE,
+                                                              new Type[0] ) );
+            } else {
+                mv.visitMethodInsn( Opcodes.INVOKEVIRTUAL,
+                                    Type.getInternalName( clazz ),
+                                    "hashCode",
+                                    Type.getMethodDescriptor( Type.INT_TYPE,
+                                                              new Type[0] ) );
+            }
             mv.visitFieldInsn( Opcodes.PUTFIELD,
                                className,
                                HASHCACHE_FIELD_NAME,

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AbstractWorkingMemory.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AbstractWorkingMemory.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/AbstractWorkingMemory.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -746,12 +746,22 @@
                                 // we need to remove the handle from the map, before replacing the object
                                 // and then re-add the handle. Otherwise we may end up with a leak.
                                 this.assertMap.remove( handle );
-                                handle.setObject( object );
+                                Object oldObject = handle.getObject();
+                                if( oldObject instanceof ShadowProxy ) {
+                                    ((ShadowProxy) oldObject).setShadowedObject( object );
+                                } else {
+                                    handle.setObject( object );
+                                }
                                 this.assertMap.put( handle,
                                                     handle,
                                                     false );
                             } else {
-                                handle.setObject( object );
+                                Object oldObject = handle.getObject();
+                                if( oldObject instanceof ShadowProxy ) {
+                                    ((ShadowProxy) oldObject).setShadowedObject( object );
+                                } else {
+                                    handle.setObject( object );
+                                }
                             }
                             return handle;
                         } else {

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/EqualityKey.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/EqualityKey.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/common/EqualityKey.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -20,6 +20,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.drools.base.ShadowProxy;
+
 /**
  * Upon instantiation the EqualityKey caches the first Object's hashCode
  * this can never change. The EqualityKey has an internal datastructure 
@@ -153,7 +155,7 @@
         if ( object instanceof EqualityKey ) {
             return this == object;
         }
-
+        
         return (this.handle.getObject().equals( object ));
     }
 

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/facttemplates/FactTemplateObjectType.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/facttemplates/FactTemplateObjectType.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/facttemplates/FactTemplateObjectType.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -73,6 +73,20 @@
     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
     /**
+     * Determine if the passed <code>Class</code> matches to the object type
+     * defined by this <code>objectType</code> instance.
+     * 
+     * @param clazz
+     *            The <code>Class</code> to test.
+     * 
+     * @return <code>true</code> if the <code>Class</code> matches this
+     *         object type, else <code>false</code>.
+     */
+    public boolean matchesClass(final Class clazz) {
+        return FactImpl.class.isAssignableFrom( clazz );
+    }
+
+    /**
      * Determine if the passed <code>Object</code> belongs to the object type
      * defined by this <code>objectType</code> instance.
      * 

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/ObjectTypeNode.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -128,6 +128,18 @@
     }
 
     /**
+     * Tests the provided class to see if this <code>ObjectTypeNode</code> can receive instances of that class
+     * for assertion and retraction propagations.
+     * 
+     * @param object
+     * @return
+     *      boolean value indicating whether the <code>ObjectTypeNode</code> can receive instances of the class.
+     */
+    public boolean matchesClass(final Class clazz) {
+        return this.objectType.matchesClass( clazz );
+    }
+
+    /**
      * Propagate the <code>FactHandleimpl</code> through the <code>Rete</code> network. All
      * <code>FactHandleImpl</code> should be remembered in the node memory, so that later runtime rule attachmnents
      * can have the matched facts propagated to them.

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/Rete.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/Rete.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/reteoo/Rete.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -187,7 +187,7 @@
 
         ObjectTypeConf objectTypeConf;
         if ( object instanceof ShadowProxy ) {
-            objectTypeConf = (ObjectTypeConf) memory.get( object.getClass().getSuperclass() );
+            objectTypeConf = (ObjectTypeConf) memory.get( ((ShadowProxy)object).getShadowedObject().getClass() );
         } else {
             objectTypeConf = (ObjectTypeConf) memory.get( object.getClass() );
         }
@@ -310,49 +310,114 @@
 
         //private final InternalRuleBase ruleBase;
 
-        public ObjectTypeConf(Class cls,
+        public ObjectTypeConf(Class clazz,
                               InternalRuleBase ruleBase) {
-            this.cls = cls;
+            this.cls = clazz;
             this.ruleBase = ruleBase;
-            Rete rete = ruleBase.getRete();
 
-            if ( !ruleBase.getConfiguration().isShadowProxy() || cls == null || !ruleBase.getConfiguration().isShadowed( cls.getName() ) ) {
+            defineShadowProxyData( clazz );
+        }
+
+        private void defineShadowProxyData(Class clazz) {
+            Rete rete = this.ruleBase.getRete();
+
+            if ( !ruleBase.getConfiguration().isShadowProxy() || clazz == null || !ruleBase.getConfiguration().isShadowed( clazz.getName() ) ) {
+                this.shadowEnabled = false;
+                this.shadowClass = null;
+                this.instantiator = null;
                 return;
             }
-            
-            Package pkg = cls.getPackage();
+
+            Package pkg = clazz.getPackage();
             String pkgName = (pkg != null) ? pkg.getName() : "";
             if ( "org.drools.reteoo".equals( pkgName ) || "org.drools.base".equals( pkgName ) ) {
                 // We don't shadow internal classes
                 this.shadowEnabled = false;
+                this.shadowClass = null;
+                this.instantiator = null;
                 return;
             }
 
+            // try to generate proxy for the actual class
+            Class shadowClass = loadOrGenerateProxy( clazz,
+                                                     rete );
+
+            if ( shadowClass == null ) {
+                // if it failed, try to find a parent class
+                ObjectTypeNode[] nodes = this.getMatchingObjectTypes( clazz );
+                Class shadowClassRoot = clazz;
+                while ( shadowClass == null && (shadowClassRoot = this.findAFeasibleSuperclassOrInterface( nodes,
+                                                                                                           shadowClassRoot )) != null ) {
+                    shadowClass = loadOrGenerateProxy( shadowClassRoot,
+                                                       rete );
+                }
+            }
+
+            if ( shadowClass != null ) {
+                this.shadowClass = shadowClass;
+                this.shadowEnabled = true;
+                setInstantiator();
+            }
+        }
+
+        private Class loadOrGenerateProxy(Class clazz,
+                                          Rete rete) {
             Class shadowClass = null;
-            final String shadowProxyName = ShadowProxyFactory.getProxyClassNameForClass( this.cls );
+            final String shadowProxyName = ShadowProxyFactory.getProxyClassNameForClass( clazz );
             try {
                 // if already loaded
                 shadowClass = rete.getRuleBase().getMapBackedClassLoader().loadClass( shadowProxyName );
             } catch ( final ClassNotFoundException cnfe ) {
                 // otherwise, create and load
-                final byte[] proxyBytes = ShadowProxyFactory.getProxyBytes( cls );
+                final byte[] proxyBytes = ShadowProxyFactory.getProxyBytes( clazz );
                 if ( proxyBytes != null ) {
                     rete.getRuleBase().getMapBackedClassLoader().addClass( shadowProxyName,
                                                                            proxyBytes );
                     try {
                         shadowClass = rete.getRuleBase().getMapBackedClassLoader().loadClass( shadowProxyName );
                     } catch ( ClassNotFoundException e ) {
-                        throw new RuntimeException( "Unable to find or generate the ShadowProxy implementation for '" + this.cls.getName() + "'" );
+                        throw new RuntimeException( "Unable to find or generate the ShadowProxy implementation for '" + clazz + "'" );
                     }
                 }
 
             }
+            return shadowClass;
+        }
 
-            if ( shadowClass != null ) {
-                this.shadowClass = shadowClass;
-                this.shadowEnabled = true;
-                setInstantiator();
+        private Class findAFeasibleSuperclassOrInterface(ObjectTypeNode[] nodes,
+                                                         Class clazz) {
+
+            // check direct superclass  
+            Class ret = clazz.getSuperclass();
+            boolean isOk = ret != null && ret != Object.class; // we don't want to shadow java.lang.Object
+            if ( isOk ) {
+                for ( int i = 0; isOk && ret != null && i < nodes.length; i++ ) {
+                    isOk = nodes[i].matchesClass( ret );
+                }
             }
+
+            if ( !isOk ) {
+                // try the interfaces now...
+                Class[] interfaces = clazz.getInterfaces();
+                boolean notFound = true;
+                isOk = interfaces.length > 0;
+                for ( int i = 0; notFound && i < interfaces.length; i++ ) {
+                    ret = interfaces[i];
+                    isOk = interfaces[i] != Serializable.class &&
+                           interfaces[i] != Cloneable.class &&
+                           interfaces[i] != Comparable.class;
+                    for ( int j = 0; isOk && j < nodes.length; j++ ) {
+                        isOk = nodes[j].matchesClass( ret );
+                    }
+                    notFound = !isOk;
+                }
+                if( notFound ) {
+                    ret = null;
+                }
+            }
+
+            // ret now contains a superclass/interface that can be shadowed or null if none
+            return ret;
         }
 
         private void readObject(ObjectInputStream stream) throws IOException,
@@ -372,16 +437,15 @@
             ShadowProxy proxy = null;
             if ( isShadowEnabled() ) {
                 try {
-                    if( Collection.class.isAssignableFrom( this.shadowClass ) ||
-                        Map.class.isAssignableFrom( this.shadowClass ) ) {
+                    if ( Collection.class.isAssignableFrom( this.shadowClass ) || Map.class.isAssignableFrom( this.shadowClass ) ) {
                         // if it is a collection, try to instantiate using constructor
                         try {
-                            proxy = (ShadowProxy) this.shadowClass.getConstructor( new Class[] { cls } ).newInstance( new Object[] { fact } );
+                            proxy = (ShadowProxy) this.shadowClass.getConstructor( new Class[]{cls} ).newInstance( new Object[]{fact} );
                         } catch ( Exception e ) {
                             // not possible to instantiate using constructor
                         }
                     }
-                    if( proxy == null ) {
+                    if ( proxy == null ) {
                         if ( this.instantiator == null ) {
                             this.setInstantiator();
                         }
@@ -402,6 +466,7 @@
 
         public void resetCache() {
             this.objectTypeNodes = null;
+            defineShadowProxyData( cls );
         }
 
         public ObjectTypeNode[] getObjectTypeNodes(final Object object) {
@@ -411,6 +476,20 @@
             return this.objectTypeNodes;
         }
 
+        private ObjectTypeNode[] getMatchingObjectTypes(final Class clazz) throws FactException {
+            final List cache = new ArrayList();
+
+            final Iterator it = ruleBase.getRete().getObjectTypeNodes().newIterator();
+            for ( ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it.next() ) {
+                final ObjectTypeNode node = (ObjectTypeNode) entry.getValue();
+                if ( node.matchesClass( clazz ) ) {
+                    cache.add( node );
+                }
+            }
+
+            return (ObjectTypeNode[]) cache.toArray( new ObjectTypeNode[cache.size()] );
+        }
+
         private void buildCache(final Object object) throws FactException {
             final List cache = new ArrayList();
 

Modified: labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/ObjectType.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/ObjectType.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/main/java/org/drools/spi/ObjectType.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -31,6 +31,18 @@
     extends
     Serializable {
     /**
+     * Determine if the passed <code>Class</code> matches to the object type
+     * defined by this <code>objectType</code> instance.
+     * 
+     * @param clazz
+     *            The <code>Class</code> to test.
+     * 
+     * @return <code>true</code> if the <code>Class</code> matches this
+     *         object type, else <code>false</code>.
+     */
+    boolean matchesClass(Class clazz);
+
+    /**
      * Determine if the passed <code>Object</code> belongs to the object type
      * defined by this <code>objectType</code> instance.
      * 

Added: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/CheeseEqual.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/CheeseEqual.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/CheeseEqual.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -0,0 +1,91 @@
+package org.drools;
+
+import java.io.Serializable;
+
+/*
+ * Copyright 2005 JBoss Inc
+ * 
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class CheeseEqual
+    implements
+    Serializable {
+    /**
+     * 
+     */
+    private static final long serialVersionUID = 400L;
+    protected String          type;
+    protected int             price;
+
+    public CheeseEqual() {
+
+    }
+
+    public CheeseEqual(final String type,
+                       final int price) {
+        super();
+        this.type = type;
+        this.price = price;
+    }
+
+    public int getPrice() {
+        return this.price;
+    }
+
+    public String getType() {
+        return this.type;
+    }
+
+    public void setPrice(final int price) {
+        this.price = price;
+    }
+
+    public String toString() {
+        return "CheeseEqual( type='" + this.type + "', price=" + this.price + " )";
+    }
+
+    public boolean equals(final Object object) {
+        if ( this == object ) {
+            return true;
+        }
+
+        if ( (object == null) || (object.getClass() != this.getClass()) ) {
+            return false;
+        }
+
+        final CheeseEqual other = (CheeseEqual) object;
+
+        if ( !this.type.equals( other.type ) ) {
+            return false;
+        }
+
+        if ( this.price != other.price ) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public int hashCode() {
+        //like org.apache.commons.lang.builder.HashCodeBuilder
+        int hashCode = 17;
+        hashCode = hashCode * 37 + this.price;
+        hashCode = hashCode * 37 + (this.type == null ? 0 : this.type.hashCode());
+        return hashCode;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+}
\ No newline at end of file

Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/base/ShadowProxyFactoryTest.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -10,6 +10,7 @@
 
 import org.drools.Address;
 import org.drools.Cheese;
+import org.drools.CheeseEqual;
 import org.drools.CheeseInterface;
 import org.drools.Person;
 
@@ -201,9 +202,9 @@
             Assert.assertEquals( cheeseHash,
                                  cheeseProxy1.hashCode() );
 
-            // now they are different
-            Assert.assertFalse( cheeseProxy1.equals( cheeseProxy2 ) );
-            Assert.assertFalse( cheeseProxy2.equals( cheeseProxy1 ) );
+            // they are still identity equals
+            Assert.assertTrue( cheeseProxy1.equals( cheeseProxy2 ) );
+            Assert.assertTrue( cheeseProxy2.equals( cheeseProxy1 ) );
 
             // updating proxy2
             ((ShadowProxy) cheeseProxy2).updateProxy();
@@ -557,5 +558,98 @@
             fail( "Error: " + e.getMessage() );
         }
     }
-    
+
+    public void testProxyForInterface2() {
+        try {
+            // creating original object
+            final String original = "stilton";
+
+            // creating proxy
+            final Class proxy = ShadowProxyFactory.getProxy( Comparable.class );
+            final Comparable comparableProxy = (Comparable) proxy.getConstructor( new Class[]{Comparable.class} ).newInstance( new Object[]{original} );
+
+            // proxy is proxying the values
+            Assert.assertEquals( comparableProxy,
+                                 original );
+            Assert.assertSame( original,
+                               ((ShadowProxy) comparableProxy).getShadowedObject() );
+            Assert.assertEquals( original.hashCode(),
+                                 comparableProxy.hashCode() );
+
+        } catch ( final Exception e ) {
+            fail( "Error: " + e.getMessage() );
+        }
+    }
+
+    public void testProxyForClassWithEquals() {
+        try {
+            // creating original object
+            final String originalType = "stilton";
+            final int originalPrice = 15;
+            final CheeseEqual cheese = new CheeseEqual( originalType,
+                                                        originalPrice );
+
+            // creating proxy
+            final Class proxy = ShadowProxyFactory.getProxy( CheeseEqual.class );
+            final CheeseEqual cheeseProxy = (CheeseEqual) proxy.getConstructor( new Class[]{CheeseEqual.class} ).newInstance( new Object[]{cheese} );
+
+            // proxy is proxying the values
+            Assert.assertEquals( originalType,
+                                 cheeseProxy.getType() );
+            Assert.assertEquals( originalPrice,
+                                 cheeseProxy.getPrice() );
+            Assert.assertSame( cheese,
+                               ((ShadowProxy) cheeseProxy).getShadowedObject() );
+
+            // proxy must recongnize the original object on equals()/hashcode() calls
+            //Assert.assertEquals( cheeseProxy.hashCode(), cheese.hashCode() );
+            Assert.assertEquals( cheeseProxy,
+                                 cheese );
+
+            // changing original values
+            final String actualType = "rotten stilton";
+            final int actualPrice = 1;
+            cheese.setType( actualType );
+            cheese.setPrice( actualPrice );
+
+            // proxy does not see changes
+            Assert.assertEquals( actualType,
+                                 cheese.getType() );
+            Assert.assertFalse( actualType.equals( cheeseProxy.getType() ) );
+            Assert.assertEquals( originalType,
+                                 cheeseProxy.getType() );
+            Assert.assertEquals( actualPrice,
+                                 cheese.getPrice() );
+            Assert.assertFalse( actualPrice == cheeseProxy.getPrice() );
+            Assert.assertEquals( originalPrice,
+                                 cheeseProxy.getPrice() );
+
+            // reseting proxy
+            ((ShadowProxy) cheeseProxy).updateProxy();
+
+            // now proxy see changes
+            Assert.assertEquals( actualType,
+                                 cheese.getType() );
+            Assert.assertEquals( actualType,
+                                 cheeseProxy.getType() );
+            Assert.assertFalse( originalType.equals( cheeseProxy.getType() ) );
+            Assert.assertEquals( actualPrice,
+                                 cheese.getPrice() );
+            Assert.assertEquals( actualPrice,
+                                 cheeseProxy.getPrice() );
+            Assert.assertFalse( originalPrice == cheeseProxy.getPrice() );
+
+            // Another cheese
+            final CheeseEqual cheese2 = new CheeseEqual( "brie",
+                                                        10 );
+            
+            final CheeseEqual cheese2Proxy = (CheeseEqual) proxy.getConstructor( new Class[]{CheeseEqual.class} ).newInstance( new Object[]{cheese2} );
+            assertFalse( cheeseProxy.equals( cheese2Proxy ) );
+            assertFalse( cheese2Proxy.equals( cheeseProxy ) );
+
+        } catch ( final Exception e ) {
+            fail( "Error: " + e.getMessage() );
+        }
+    }
+
 }

Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/reteoo/ObjectTypeNodeTest.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -320,10 +320,6 @@
     }
 
     public void testAssertObjectWithShadowEnabled() throws Exception {
-        final PropagationContext context = new PropagationContextImpl( 0,
-                                                                       PropagationContext.ASSERTION,
-                                                                       null,
-                                                                       null );
 
         final ReteooRuleBase ruleBase = (ReteooRuleBase) RuleBaseFactory.newRuleBase();
         final ReteooWorkingMemory workingMemory = new ReteooWorkingMemory( 1,
@@ -331,7 +327,6 @@
 
         final Rete source = ruleBase.getRete();
 
-        final Class shadowClass = ShadowProxyFactory.getProxy( Cheese.class );
         final ObjectTypeNode objectTypeNode = new ObjectTypeNode( 1,
                                                                   new ClassObjectType( Cheese.class  ),
                                                                   source,
@@ -339,17 +334,13 @@
 
         final MockObjectSink sink = new MockObjectSink();
         objectTypeNode.addObjectSink( sink );
+        source.addObjectSink( objectTypeNode );
 
         final Object cheese = new Cheese( "muzzarela",
                                           5 );
 
         final InternalFactHandle handle1 = (InternalFactHandle) workingMemory.insert( cheese );
 
-        // should assert as ObjectType matches
-        objectTypeNode.assertObject( handle1,
-                                     context,
-                                     workingMemory );
-
         // make sure just string1 was asserted 
         final List asserted = sink.getAsserted();
         assertLength( 1,

Modified: labs/jbossrules/trunk/drools-core/src/test/java/org/drools/spi/MockObjectType.java
===================================================================
--- labs/jbossrules/trunk/drools-core/src/test/java/org/drools/spi/MockObjectType.java	2007-07-18 01:57:17 UTC (rev 13590)
+++ labs/jbossrules/trunk/drools-core/src/test/java/org/drools/spi/MockObjectType.java	2007-07-18 02:15:24 UTC (rev 13591)
@@ -62,6 +62,20 @@
     // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
     /**
+     * Determine if the passed <code>Class</code> matches to the object type
+     * defined by this <code>objectType</code> instance.
+     * 
+     * @param clazz
+     *            The <code>Class</code> to test.
+     * 
+     * @return <code>true</code> if the <code>Class</code> matches this
+     *         object type, else <code>false</code>.
+     */
+    public boolean matchesClass(final Class clazz) {
+        return this.matches;
+    }
+
+    /**
      * Determine if the passed <code>Object</code> belongs to the object type
      * defined by this <code>objectType</code> instance.
      * 




More information about the jboss-svn-commits mailing list