I played with Simone's code a bit, and it does seem to work as described.

Here's the solution I came up with for when the constructor call is
made within a constructor call.

This solution works assuming that the only places where A or it's
subclasses are instantiated within the constructor of A or it's
subclasses are in code that can be woven, which, for my project, is a
safe assumption.

I made the class abstract so that it can be easily reused whenever
this pattern is appropriate.

I don't like having the getMyClass() method, but I don't know of a
good way to get the generic type without it.

I am also caching the return value from isAssignableFrom, since this
call appears to be relatively expensive in some versions of java.

It's still a bit of a hack, but it seems to fulfill my needs.

public abstract aspect InitializationAspect<T> {
        /* Cache to avoid reflective calls */
        private Map<String, Boolean> isAssignableMap = new HashMap<String, 
Boolean>();
        
        /**
         * Returns the class highest in the hierarchy.  Should be T.
         */
        protected abstract Class<T> getMyClass();
        
        /**
         * The Advice to perform after an object has been instantiated.
         * @param t The object that was created.
         */
        protected abstract void myAdvice(T t);
        
        /**
         * Checks to make sure that the constructor that is executing
         * is the type of the object to be created (i.e. lowest on the stack)
         * and calls myAdvice() if it is. Does nothing if not.
         * @param t The object that was created
         */
        after(T t) : execution(T+.new(..)) && this(t)  {
                if (isLastConstructorInChain(getMyClass())) {
                        myAdvice(t);
                }
        }
        
        /**
         * Captures any calls to create a new object of type T or it's subtypes
         * that happen within type T or it's subtypes, and calls myAdvice().
         */
        after() returning(T t): call(T+.new()) && withincode(T+.new(..)) {
                myAdvice(t);
        }
        
        /**
         * Returns true if a class' constructor the lowest method call in a
         * chain of constructor calls.  If it isn't, return true if the next 
lowest
         * call is not type T or a subclass of type T, false otherwise.
         * @param clazz The class to look for.
         */
        public boolean isLastConstructorInChain(Class<?> clazz) {
                StackTraceElement[] stackTrace =
                        Thread.currentThread().getStackTrace();
                
                // find the first constructor, cause there are a few AspectJ
internal calls before it
                int acpos = 0;
                while (acpos < stackTrace.length &&
                                
!stackTrace[acpos].getMethodName().equals("<init>")) {
                        acpos++;
                }
                // Check if we run out, should never happen
                if (acpos >= stackTrace.length) {
                        return false;
                }
                // Now we have the last call to <init>
                // if the following one is not a constructor (or is not 
present),
                // we can skip all the checks and return true
                if (acpos + 1 == stackTrace.length ||
                                !stackTrace[acpos + 
1].getMethodName().equals("<init>")) {
                        return true;    
                }
                // Otherwise we have to check it the next constructor in the
                // chain is a proper subclass of the current class
                String callingClass = stackTrace[acpos + 1].getClassName();
                
                Boolean isAssignable = isAssignableMap.get(callingClass);
                
                if(isAssignable == null) {
                        Class<?> forname = null;
                        try {
                                forname = Class.forName(callingClass);
                        }
                        catch (ClassNotFoundException e) {
                                e.printStackTrace();
                                isAssignable = false;
                        }
                        isAssignable = !clazz.isAssignableFrom(forname);
                        
                        isAssignableMap.put(callingClass, isAssignable);
                }
                return isAssignable;
        }
}

public aspect AAspect extends InitializationAspect<A> {
        protected Class<A> getMyClass() {
                return A.class;
        }
        
        protected void myAdvice(A a) {
                System.out.println("Done:" + a.getClass());
        }
}
_______________________________________________
aspectj-users mailing list
[email protected]
https://dev.eclipse.org/mailman/listinfo/aspectj-users

Reply via email to