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