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;
021
022import java.lang.reflect.Method;
023
024import org.apache.isis.core.commons.lang.StringExtensions;
025import org.apache.isis.core.metamodel.adapter.mgr.AdapterManager;
026import org.apache.isis.core.metamodel.adapter.mgr.AdapterManagerAware;
027import org.apache.isis.core.metamodel.facetapi.Facet;
028import org.apache.isis.core.metamodel.facetapi.FacetHolder;
029import org.apache.isis.core.metamodel.facetapi.FacetUtil;
030import org.apache.isis.core.metamodel.facetapi.FeatureType;
031import org.apache.isis.core.metamodel.facets.actions.debug.DebugFacet;
032import org.apache.isis.core.metamodel.facets.actions.exploration.ExplorationFacet;
033import org.apache.isis.core.metamodel.facets.actions.invoke.ActionInvocationFacet;
034import org.apache.isis.core.metamodel.facets.named.NamedFacet;
035import org.apache.isis.core.metamodel.facets.named.NamedFacetInferred;
036import org.apache.isis.core.metamodel.runtimecontext.RuntimeContext;
037import org.apache.isis.core.metamodel.runtimecontext.RuntimeContextAware;
038import org.apache.isis.core.metamodel.runtimecontext.ServicesInjector;
039import org.apache.isis.core.metamodel.runtimecontext.ServicesInjectorAware;
040import org.apache.isis.core.metamodel.spec.ObjectSpecification;
041import org.apache.isis.core.progmodel.facets.MethodPrefixBasedFacetFactoryAbstract;
042
043/**
044 * Sets up {@link ActionInvocationFacet}, along with a number of supporting
045 * facets that are based on the action's name.
046 * 
047 * <p>
048 * The supporting methods are: {@link ExecutedFacet}, {@link ExplorationFacet}
049 * and {@link DebugFacet}. In addition a {@link NamedFacet} is inferred from the
050 * name (taking into account the above well-known prefixes).
051 */
052public class ActionInvocationFacetFactory extends MethodPrefixBasedFacetFactoryAbstract implements AdapterManagerAware, ServicesInjectorAware, RuntimeContextAware {
053
054    private static final String EXPLORATION_PREFIX = "Exploration";
055    private static final String DEBUG_PREFIX = "Debug";
056
057    private static final String[] PREFIXES = { EXPLORATION_PREFIX, DEBUG_PREFIX };
058
059    private AdapterManager adapterManager;
060    private ServicesInjector servicesInjector;
061    private RuntimeContext runtimeContext;
062
063    /**
064     * Note that the {@link Facet}s registered are the generic ones from
065     * noa-architecture (where they exist)
066     */
067    public ActionInvocationFacetFactory() {
068        super(FeatureType.ACTIONS_ONLY, OrphanValidation.VALIDATE, PREFIXES);
069    }
070
071    // ///////////////////////////////////////////////////////
072    // Actions
073    // ///////////////////////////////////////////////////////
074
075    @Override
076    public void process(final ProcessMethodContext processMethodContext) {
077
078        // InvocationFacet
079        attachInvocationFacet(processMethodContext);
080
081        // DebugFacet, ExplorationFacet
082        attachDebugFacetIfActionMethodNamePrefixed(processMethodContext);
083        attachExplorationFacetIfActionMethodNamePrefixed(processMethodContext);
084
085        // inferred name
086        // (must be called after the attachinvocationFacet methods)
087        attachNamedFacetInferredFromMethodName(processMethodContext); 
088    }
089
090    private void attachInvocationFacet(final ProcessMethodContext processMethodContext) {
091
092        final Method actionMethod = processMethodContext.getMethod();
093
094        try {
095            final Class<?> returnType = actionMethod.getReturnType();
096            final ObjectSpecification returnSpec = getSpecificationLoader().loadSpecification(returnType);
097            if (returnSpec == null) {
098                return;
099            }
100
101            final Class<?> cls = processMethodContext.getCls();
102            final ObjectSpecification typeSpec = getSpecificationLoader().loadSpecification(cls);
103            final FacetHolder holder = processMethodContext.getFacetHolder();
104
105            FacetUtil.addFacet(new ActionInvocationFacetViaMethod(actionMethod, typeSpec, returnSpec, holder, getRuntimeContext(), getAdapterManager(), getServicesInjector()));
106        } finally {
107            processMethodContext.removeMethod(actionMethod);
108        }
109    }
110
111    private void attachDebugFacetIfActionMethodNamePrefixed(final ProcessMethodContext processMethodContext) {
112
113        final Method actionMethod = processMethodContext.getMethod();
114        final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName());
115        if (!capitalizedName.startsWith(DEBUG_PREFIX)) {
116            return;
117        }
118        final FacetHolder facetedMethod = processMethodContext.getFacetHolder();
119        FacetUtil.addFacet(new DebugFacetViaNamingConvention(facetedMethod));
120    }
121
122    private void attachExplorationFacetIfActionMethodNamePrefixed(final ProcessMethodContext processMethodContext) {
123
124        final Method actionMethod = processMethodContext.getMethod();
125        final String capitalizedName = StringExtensions.asCapitalizedName(actionMethod.getName());
126        if (!capitalizedName.startsWith(EXPLORATION_PREFIX)) {
127            return;
128        }
129        final FacetHolder facetedMethod = processMethodContext.getFacetHolder();
130        FacetUtil.addFacet(new ExplorationFacetViaNamingConvention(facetedMethod));
131    }
132
133    /**
134     * Must be called after added the debug, exploration etc facets.
135     * 
136     * <p>
137     * TODO: remove this hack
138     */
139    private void attachNamedFacetInferredFromMethodName(final ProcessMethodContext processMethodContext) {
140
141        final Method method = processMethodContext.getMethod();
142        final String capitalizedName = StringExtensions.asCapitalizedName(method.getName());
143
144        // this is nasty...
145        String name = capitalizedName;
146        name = StringExtensions.removePrefix(name, DEBUG_PREFIX);
147        name = StringExtensions.removePrefix(name, EXPLORATION_PREFIX);
148        name = StringExtensions.asNaturalName2(name);
149
150        final FacetHolder facetedMethod = processMethodContext.getFacetHolder();
151        FacetUtil.addFacet(new NamedFacetInferred(name, facetedMethod));
152    }
153
154    // ///////////////////////////////////////////////////////////////
155    // Dependencies
156    // ///////////////////////////////////////////////////////////////
157
158    @Override
159    public void setAdapterManager(final AdapterManager adapterManager) {
160        this.adapterManager = adapterManager;
161    }
162
163    private AdapterManager getAdapterManager() {
164        return adapterManager;
165    }
166
167    @Override
168    public void setServicesInjector(final ServicesInjector servicesInjector) {
169        this.servicesInjector = servicesInjector;
170    }
171
172    private ServicesInjector getServicesInjector() {
173        return servicesInjector;
174    }
175
176    private RuntimeContext getRuntimeContext() {
177        return runtimeContext;
178    }
179    
180    @Override
181    public void setRuntimeContext(RuntimeContext runtimeContext) {
182        this.runtimeContext = runtimeContext;
183    }
184}