001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.progmodel.facets.actions.invoke.event;
021
022import java.lang.reflect.Method;
023
024import org.apache.isis.applib.annotation.PostsActionInvokedEvent;
025import org.apache.isis.applib.annotation.PostsCollectionAddedToEvent;
026import org.apache.isis.applib.services.eventbus.ActionInvokedEvent;
027import org.apache.isis.applib.services.eventbus.CollectionAddedToEvent;
028import org.apache.isis.core.commons.lang.StringExtensions;
029import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
030import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware;
031import org.apache.isis.core.metamodel.facetapi.Facet;
032import org.apache.isis.core.metamodel.facetapi.FacetHolder;
033import org.apache.isis.core.metamodel.facetapi.FacetUtil;
034import org.apache.isis.core.metamodel.facetapi.FeatureType;
035import org.apache.isis.core.metamodel.facets.Annotations;
036import org.apache.isis.core.metamodel.facets.actions.debug.DebugFacet;
037import org.apache.isis.core.metamodel.facets.actions.exploration.ExplorationFacet;
038import org.apache.isis.core.metamodel.facets.actions.invoke.ActionInvocationFacet;
039import org.apache.isis.core.metamodel.facets.named.NamedFacet;
040import org.apache.isis.core.metamodel.facets.named.NamedFacetInferred;
041import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
042import org.apache.isis.core.metamodel.runtimecontext.RuntimeContextAware;
043import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
044import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
045import org.apache.isis.core.metamodel.spec.ObjectSpecification;
046import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
047import org.apache.isis.core.progmodel.facets.actions.invoke.DebugFacetViaNamingConvention;
048import org.apache.isis.core.progmodel.facets.actions.invoke.ExplorationFacetViaNamingConvention;
049
050/**
051 * Sets up {@link ActionInvocationFacet}, along with a number of supporting
052 * facets that are based on the action's name.
053 * 
054 * <p>
055 * The supporting methods are: {@link ExecutedFacet}, {@link ExplorationFacet}
056 * and {@link DebugFacet}. In addition a {@link NamedFacet} is inferred from the
057 * name (taking into account the above well-known prefixes).
058 */
059public class PostsActionInvokedEventFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements AdapterManagerAware, ServicesInjectorAware, RuntimeContextAware {
060
061    private static final String EXPLORATION_PREFIX = "Exploration";
062    private static final String DEBUG_PREFIX = "Debug";
063
064    private static final String[] PREFIXES = { EXPLORATION_PREFIX, DEBUG_PREFIX };
065
066    private AdapterManager adapterManager;
067    private ServicesInjector servicesInjector;
068    private RuntimeContext runtimeContext;
069
070    /**
071     * Note that the {@link Facet}s registered are the generic ones from
072     * noa-architecture (where they exist)
073     */
074    public PostsActionInvokedEventFacetFactory() {
075        super(FeatureType.ACTIONS_ONLY, OrphanValidation.VALIDATE, PREFIXES);
076    }
077
078    // ///////////////////////////////////////////////////////
079    // Actions
080    // ///////////////////////////////////////////////////////
081
082    @Override
083    public void process(final ProcessMethodContext processMethodContext) {
084
085        // InvocationFacet
086        attachInvocationFacet(processMethodContext);
087
088        // DebugFacet, ExplorationFacet
089        attachDebugFacetIfActionMethodNamePrefixed(processMethodContext);
090        attachExplorationFacetIfActionMethodNamePrefixed(processMethodContext);
091
092        // inferred name
093        // (must be called after the attachinvocationFacet methods)
094        attachNamedFacetInferredFromMethodName(processMethodContext); 
095    }
096
097    private void attachInvocationFacet(final ProcessMethodContext processMethodContext) {
098
099        final Method actionMethod = processMethodContext.getMethod();
100
101        try {
102            final PostsActionInvokedEvent annotation = Annotations.getAnnotation(actionMethod, PostsActionInvokedEvent.class);
103            if(annotation == null) {
104                return;
105            }
106                
107            final Class<?> returnType = actionMethod.getReturnType();
108            final ObjectSpecification returnSpec = getSpecificationLoader().loadSpecification(returnType);
109            if (returnSpec == null) {
110                return;
111            }
112
113            final Class<?> cls = processMethodContext.getCls();
114            final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls);
115            final FacetHolder holder = processMethodContext.getFacetHolder();
116            
117            final Class<? extends ActionInvokedEvent<?>> changedEventType = annotation.value();
118
119            FacetUtil.addFacet(new PostsActionInvokedEventFacetAnnotation(actionMethod, typeSpec, returnSpec, holder, getRuntimeContext(), getAdapterManager(), getServicesInjector(), changedEventType));
120        } finally {
121            processMethodContext.removeMethod(actionMethod);
122        }
123    }
124
125    private void attachDebugFacetIfActionMethodNamePrefixed(final ProcessMethodContext processMethodContext) {
126
127        final Method actionMethod = processMethodContext.getMethod();
128        final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName());
129        if (!capitalizedName.startsWith(DEBUG_PREFIX)) {
130            return;
131        }
132        final FacetHolder facetedMethod = processMethodContext.getFacetHolder();
133        FacetUtil.addFacet(new DebugFacetViaNamingConvention(facetedMethod));
134    }
135
136    private void attachExplorationFacetIfActionMethodNamePrefixed(final ProcessMethodContext processMethodContext) {
137
138        final Method actionMethod = processMethodContext.getMethod();
139        final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName());
140        if (!capitalizedName.startsWith(EXPLORATION_PREFIX)) {
141            return;
142        }
143        final FacetHolder facetedMethod = processMethodContext.getFacetHolder();
144        FacetUtil.addFacet(new ExplorationFacetViaNamingConvention(facetedMethod));
145    }
146
147    /**
148     * Must be called after added the debug, exploration etc facets.
149     * 
150     * <p>
151     * TODO: remove this hack
152     */
153    private void attachNamedFacetInferredFromMethodName(final ProcessMethodContext processMethodContext) {
154
155        final Method method = processMethodContext.getMethod();
156        final String capitalizedName = StringExtensions.asCapitalizedName(method.getName());
157
158        // this is nasty...
159        String name = capitalizedName;
160        name = StringExtensions.removePrefix(name, DEBUG_PREFIX);
161        name = StringExtensions.removePrefix(name, EXPLORATION_PREFIX);
162        name = StringExtensions.asNaturalName2(name);
163
164        final FacetHolder facetedMethod = processMethodContext.getFacetHolder();
165        FacetUtil.addFacet(new NamedFacetInferred(name, facetedMethod));
166    }
167
168    // ///////////////////////////////////////////////////////////////
169    // Dependencies
170    // ///////////////////////////////////////////////////////////////
171
172    @Override
173    public void setAdapterManager(final AdapterManager adapterManager) {
174        this.adapterManager = adapterManager;
175    }
176
177    private AdapterManager getAdapterManager() {
178        return adapterManager;
179    }
180
181    @Override
182    public void setServicesInjector(final ServicesInjector servicesInjector) {
183        this.servicesInjector = servicesInjector;
184    }
185
186    private ServicesInjector getServicesInjector() {
187        return servicesInjector;
188    }
189
190    private RuntimeContext getRuntimeContext() {
191        return runtimeContext;
192    }
193    
194    @Override
195    public void setRuntimeContext(RuntimeContext runtimeContext) {
196        this.runtimeContext = runtimeContext;
197    }
198}