001    /*****************************************************************************
002     * Copyright (C) NanoContainer Organization. All rights reserved.            *
003     * ------------------------------------------------------------------------- *
004     * The software in this package is published under the terms of the BSD      *
005     * style license a copy of which has been included with this distribution in *
006     * the LICENSE.txt file.                                                     *
007     *                                                                           *
008     * Original code by James Strachan                                           *
009     *****************************************************************************/
010    
011    package org.nanocontainer.aop.defaults;
012    
013    import dynaop.Aspects;
014    import dynaop.Pointcuts;
015    import dynaop.ProxyFactory;
016    import org.aopalliance.intercept.MethodInterceptor;
017    import org.nanocontainer.aop.AspectablePicoContainer;
018    import org.nanocontainer.aop.AspectsApplicator;
019    import org.nanocontainer.aop.AspectsContainer;
020    import org.nanocontainer.aop.AspectsManager;
021    import org.nanocontainer.aop.ClassPointcut;
022    import org.nanocontainer.aop.ComponentPointcut;
023    import org.nanocontainer.aop.MethodPointcut;
024    import org.nanocontainer.aop.dynaop.InstanceMixinFactory;
025    import org.nanocontainer.script.NodeBuilderDecorationDelegate;
026    import org.nanocontainer.script.NanoContainerMarkupException;
027    import org.picocontainer.MutablePicoContainer;
028    import org.picocontainer.defaults.ComponentAdapterFactory;
029    
030    import java.util.List;
031    import java.util.Map;
032    
033    /**
034     * @author Aslak Hellesøy
035     * @author Paul Hammant
036     * @version $Revision: 3144 $
037     */
038    public class AopNodeBuilderDecorationDelegate implements NodeBuilderDecorationDelegate {
039    
040        private final AspectsManager aspectsManager;
041        private Object currentKey;
042        private AspectablePicoContainer currentPico;
043        private ClassPointcut currentClassCut;
044        private MethodPointcut currentMethodCut;
045    
046        public AopNodeBuilderDecorationDelegate(AspectsManager aspectsManager) {
047            this.aspectsManager = aspectsManager;
048        }
049    
050        public ComponentAdapterFactory decorate(ComponentAdapterFactory componentAdapterFactory, Map attributes) {
051            AspectsComponentAdapterFactory aspectsComponentAdapterFactory = createAdapterFactory(aspectsManager, componentAdapterFactory);
052            return aspectsComponentAdapterFactory;
053        }
054    
055        public MutablePicoContainer decorate(MutablePicoContainer picoContainer) {
056            currentPico = mixinAspectablePicoContainer(aspectsManager, picoContainer);
057            return currentPico;
058        }
059    
060        public Object createNode(Object name, Map attributes, Object parentElement) {
061            if (name.equals("aspect")) {
062                return createAspectNode(attributes, name);
063            } else if (name.equals("pointcut")) {
064                return createPointcutNode(attributes, name);
065            } else {
066                throw new NanoContainerMarkupException("Don't know how to create a '" + name + "' child of a '" + parentElement.toString() + "' element");
067            }
068        }
069    
070        private Object createPointcutNode(Map attributes, Object name) {
071            currentClassCut = (ClassPointcut) attributes.remove("classCut");
072            currentMethodCut = (MethodPointcut) attributes.remove("methodCut");
073            return name;
074        }
075    
076        private Object createAspectNode(Map attributes, Object name) {
077            ClassPointcut classCut = (ClassPointcut) attributes.remove("classCut");
078            if(classCut != null) {
079                currentClassCut = classCut;
080            }
081            MethodPointcut methodCut = (MethodPointcut) attributes.remove("methodCut");
082            if(methodCut != null) {
083                currentMethodCut = methodCut;
084            }
085    
086            MethodInterceptor interceptor = (MethodInterceptor) attributes.remove("interceptor");
087            Object interceptorKey = attributes.remove("interceptorKey");
088            Class mixinClass = (Class) attributes.remove("mixinClass");
089            List mixinInterfaces = (List) attributes.remove("mixinInterfaces");
090    
091            ComponentPointcut componentCut = (ComponentPointcut) attributes.remove("componentCut");
092            if (componentCut == null && currentKey != null) {
093                componentCut = currentPico.getPointcutsFactory().component(currentKey);
094            }
095    
096            if (interceptor != null || interceptorKey != null) {
097                registerInterceptor(currentPico, currentClassCut, componentCut, currentMethodCut, interceptor, interceptorKey);
098            } else if (mixinClass != null) {
099                registerMixin(currentPico, currentClassCut, componentCut, toClassArray(mixinInterfaces), mixinClass);
100            } else {
101                throw new NanoContainerMarkupException("No advice specified - must specify one of interceptor, interceptorKey, mixinClass, or mixinKey");
102            }
103    
104            return name;
105        }
106    
107    
108        private AspectsComponentAdapterFactory createAdapterFactory(AspectsApplicator aspectsApplicator,
109                                                                    ComponentAdapterFactory delegateAdapterFactory) {
110            if (delegateAdapterFactory != null) {
111                return new AspectsComponentAdapterFactory(aspectsApplicator, delegateAdapterFactory);
112            } else {
113                return new AspectsComponentAdapterFactory(aspectsApplicator);
114            }
115        }
116    
117        private AspectablePicoContainer mixinAspectablePicoContainer(AspectsManager aspectsManager,
118                                                                     MutablePicoContainer pico) {
119            Aspects aspects = new Aspects();
120            aspects.mixin(Pointcuts.ALL_CLASSES, new Class[]{AspectsContainer.class}, new InstanceMixinFactory(aspectsManager));
121            aspects.interfaces(Pointcuts.ALL_CLASSES, new Class[]{AspectablePicoContainer.class});
122            return (AspectablePicoContainer) ProxyFactory.getInstance(aspects).wrap(pico);
123        }
124    
125        private void registerInterceptor(AspectablePicoContainer pico, ClassPointcut classCut,
126                                         ComponentPointcut componentCut, MethodPointcut methodCut, MethodInterceptor interceptor,
127                                         Object interceptorKey) {
128            // precondition:
129            if (interceptor == null && interceptorKey == null) {
130                throw new RuntimeException("assertion failed -- non-null interceptor or interceptorKey expected");
131            }
132    
133            // validate script:
134            if (classCut == null && componentCut == null) {
135                throw new NanoContainerMarkupException("currentClassCut or componentCut required for interceptor advice");
136            }
137            if (methodCut == null) {
138                throw new NanoContainerMarkupException("currentMethodCut required for interceptor advice");
139            }
140    
141            if (classCut != null) {
142                if (interceptor != null) {
143                    pico.registerInterceptor(classCut, methodCut, interceptor);
144                } else {
145                    pico.registerInterceptor(classCut, methodCut, interceptorKey);
146                }
147            } else {
148                if (interceptor != null) {
149                    pico.registerInterceptor(componentCut, methodCut, interceptor);
150                } else {
151                    pico.registerInterceptor(componentCut, methodCut, interceptorKey);
152                }
153            }
154        }
155    
156        private void registerMixin(AspectablePicoContainer pico, ClassPointcut classCut, ComponentPointcut componentCut,
157                                   Class[] mixinInterfaces, Class mixinClass) {
158            // precondition:
159            if (mixinClass == null) {
160                throw new RuntimeException("assertion failed -- mixinClass required");
161            }
162    
163            // validate script:
164            if (classCut == null && componentCut == null) {
165                throw new NanoContainerMarkupException("currentClassCut or componentCut required for mixin advice");
166            }
167    
168            if (classCut != null) {
169                if (mixinInterfaces != null) {
170                    pico.registerMixin(classCut, mixinInterfaces, mixinClass);
171                } else {
172                    pico.registerMixin(classCut, mixinClass);
173                }
174            } else {
175                if (mixinInterfaces != null) {
176                    pico.registerMixin(componentCut, mixinInterfaces, mixinClass);
177                } else {
178                    pico.registerMixin(componentCut, mixinClass);
179                }
180            }
181        }
182    
183        private Class[] toClassArray(List l) {
184            if (l == null) {
185                return null;
186            }
187            return (Class[]) l.toArray(new Class[l.size()]);
188        }
189    
190        public void rememberComponentKey(Map attributes) {
191            Object key = attributes.get("key");
192            Object clazz = attributes.get("class");
193            if (key != null) {
194                currentKey = key;
195            } else {
196                currentKey = clazz;
197            }
198        }
199    }