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.metamodel.spec.feature;
021
022import java.util.List;
023import com.google.common.base.Predicate;
024import com.google.common.base.Strings;
025import org.apache.isis.applib.annotation.ActionSemantics;
026import org.apache.isis.applib.annotation.Bulk;
027import org.apache.isis.applib.annotation.When;
028import org.apache.isis.applib.annotation.Where;
029import org.apache.isis.applib.filter.Filter;
030import org.apache.isis.applib.value.Blob;
031import org.apache.isis.applib.value.Clob;
032import org.apache.isis.core.commons.authentication.AuthenticationSession;
033import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
034import org.apache.isis.core.metamodel.consent.Consent;
035import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod;
036import org.apache.isis.core.metamodel.facetapi.Facet;
037import org.apache.isis.core.metamodel.facetapi.FacetFilters;
038import org.apache.isis.core.metamodel.facets.hide.HiddenFacet;
039import org.apache.isis.core.metamodel.facets.members.order.MemberOrderFacet;
040import org.apache.isis.core.metamodel.facets.named.NamedFacet;
041import org.apache.isis.core.metamodel.interactions.AccessContext;
042import org.apache.isis.core.metamodel.interactions.ActionInvocationContext;
043import org.apache.isis.core.metamodel.interactions.ValidatingInteractionAdvisor;
044import org.apache.isis.core.metamodel.spec.ActionType;
045import org.apache.isis.core.metamodel.spec.ObjectSpecification;
046import org.apache.isis.core.progmodel.facets.actions.bulk.BulkFacet;
047
048public interface ObjectAction extends ObjectMember {
049
050    // //////////////////////////////////////////////////////
051    // semantics, realTarget, getOnType
052    // //////////////////////////////////////////////////////
053
054    /**
055     * The semantics of this action.
056     */
057    ActionSemantics.Of getSemantics();
058
059    /**
060     * Returns the specification for the type of object that this action can be
061     * invoked upon.
062     */
063    ObjectSpecification getOnType();
064
065
066    boolean promptForParameters(ObjectAdapter target);
067
068    // //////////////////////////////////////////////////////////////////
069    // Type
070    // //////////////////////////////////////////////////////////////////
071
072    ActionType getType();
073
074    // //////////////////////////////////////////////////////////////////
075    // ReturnType
076    // //////////////////////////////////////////////////////////////////
077
078    /**
079     * Returns the specifications for the return type.
080     */
081    ObjectSpecification getReturnType();
082
083    /**
084     * Returns <tt>true</tt> if the represented action returns a non-void object, 
085     * else returns false.
086     */
087    boolean hasReturn();
088
089    // //////////////////////////////////////////////////////////////////
090    // execute
091    // //////////////////////////////////////////////////////////////////
092
093    /**
094     * Invokes the action's method on the target object given the specified set
095     * of parameters.
096     */
097    ObjectAdapter execute(ObjectAdapter target, ObjectAdapter[] parameters);
098
099    // //////////////////////////////////////////////////////////////////
100    // valid
101    // //////////////////////////////////////////////////////////////////
102
103    /**
104     * Creates an {@link ActionInvocationContext interaction context}
105     * representing an attempt to invoke this action.
106     * 
107     * <p>
108     * Typically it is easier to just call
109     * {@link #isProposedArgumentSetValid(ObjectAdapter, ObjectAdapter[])
110     * 
111     * @link #isProposedArgumentSetValidResultSet(ObjectAdapter,
112     *       ObjectAdapter[])}; this is provided as API for symmetry with
113     *       interactions (such as {@link AccessContext} accesses) have no
114     *       corresponding vetoing methods.
115     */
116    public ActionInvocationContext createActionInvocationInteractionContext(AuthenticationSession session, InteractionInvocationMethod invocationMethod, ObjectAdapter targetObject, ObjectAdapter[] proposedArguments);
117
118    /**
119     * Whether the provided argument set is valid, represented as a
120     * {@link Consent}.
121     */
122    Consent isProposedArgumentSetValid(ObjectAdapter object, ObjectAdapter[] proposedArguments);
123
124    // //////////////////////////////////////////////////////
125    // Parameters (declarative)
126    // //////////////////////////////////////////////////////
127
128    /**
129     * Returns the number of parameters used by this method.
130     */
131    int getParameterCount();
132
133    /**
134     * Returns set of parameter information.
135     * 
136     * <p>
137     * Implementations may build this array lazily or eagerly as required.
138     * 
139     * @return
140     */
141    List<ObjectActionParameter> getParameters();
142
143    /**
144     * Returns the {@link ObjectSpecification type} of each of the
145     * {@link #getParameters() parameters}.
146     */
147    List<ObjectSpecification> getParameterTypes();
148
149    /**
150     * Returns set of parameter information matching the supplied filter.
151     * 
152     * @return
153     */
154    List<ObjectActionParameter> getParameters(
155            @SuppressWarnings("deprecation") Filter<ObjectActionParameter> filter);
156
157    /**
158     * Returns the parameter with provided id.
159     */
160    ObjectActionParameter getParameterById(String paramId);
161
162    /**
163     * Returns the parameter with provided name.
164     */
165    ObjectActionParameter getParameterByName(String paramName);
166
167    // //////////////////////////////////////////////////////
168    // Parameters (per instance)
169    // //////////////////////////////////////////////////////
170
171    /**
172     * Returns the defaults references/values to be used for the action.
173     */
174    ObjectAdapter[] getDefaults(ObjectAdapter target);
175
176    /**
177     * Returns a list of possible references/values for each parameter, which
178     * the user can choose from.
179     */
180    ObjectAdapter[][] getChoices(ObjectAdapter target);
181
182
183    // //////////////////////////////////////////////////////
184    // Utils
185    // //////////////////////////////////////////////////////
186
187    public static final class Utils {
188
189        private Utils() {
190        }
191
192        public static String nameFor(final ObjectAction objAction) {
193            final String actionName = objAction.getName();
194            if (actionName != null) {
195                return actionName;
196            }
197            final NamedFacet namedFacet = objAction.getFacet(NamedFacet.class);
198            if (namedFacet != null) {
199                return namedFacet.value();
200            }
201            return "(no name)";
202        }
203    }
204
205
206    // //////////////////////////////////////////////////////
207    // Predicates
208    // //////////////////////////////////////////////////////
209
210    public static final class Predicates {
211
212        private Predicates(){}
213
214        public static final Predicate<ObjectAction> VISIBLE_AT_LEAST_SOMETIMES =
215                org.apache.isis.applib.filter.Filters.asPredicate(Filters.VISIBLE_AT_LEAST_SOMETIMES);
216
217        public static Predicate<ObjectAction> dynamicallyVisible(final AuthenticationSession session, final ObjectAdapter target, final Where where) {
218            return org.apache.isis.applib.filter.Filters.asPredicate(Filters.dynamicallyVisible(session, target, where));
219        }
220
221        public static Predicate<ObjectAction> withId(final String actionId) {
222            return org.apache.isis.applib.filter.Filters.asPredicate(Filters.withId(actionId));
223        }
224
225        public static Predicate<ObjectAction> withNoValidationRules() {
226            return org.apache.isis.applib.filter.Filters.asPredicate(Filters.withNoValidationRules());
227        }
228
229        public static Predicate<ObjectAction> ofType(final ActionType type) {
230            return org.apache.isis.applib.filter.Filters.asPredicate(Filters.ofType(type));
231        }
232
233        public static Predicate<ObjectAction> bulk() {
234            return org.apache.isis.applib.filter.Filters.asPredicate(Filters.bulk());
235        }
236
237        public static Predicate<ObjectAction> notBulkOnly() {
238            return org.apache.isis.applib.filter.Filters.asPredicate(Filters.notBulkOnly());
239        }
240
241        public static Predicate<ObjectAction> memberOrderOf(ObjectAssociation association) {
242            return org.apache.isis.applib.filter.Filters.asPredicate(Filters.memberOrderOf(association));
243        }
244    }
245
246
247    // //////////////////////////////////////////////////////
248    // Filters
249    // //////////////////////////////////////////////////////
250
251    
252    public static final class Filters {
253        
254        private Filters(){}
255
256        /**
257         * @deprecated -use {@link com.google.common.base.Predicate equivalent}
258         */
259        @Deprecated
260        public static final Filter<ObjectAction> VISIBLE_AT_LEAST_SOMETIMES = new Filter<ObjectAction>() {
261            @Override
262            public boolean accept(final ObjectAction action) {
263                final HiddenFacet hiddenFacet = action.getFacet(HiddenFacet.class);
264                return hiddenFacet == null || hiddenFacet.when() != When.ALWAYS || hiddenFacet.where() != Where.ANYWHERE;
265            }
266        };
267
268        /**
269         * @deprecated -use {@link com.google.common.base.Predicate equivalent}
270         */
271        @Deprecated
272        public static Filter<ObjectAction> dynamicallyVisible(final AuthenticationSession session, final ObjectAdapter target, final Where where) {
273            return new Filter<ObjectAction>() {
274                @Override
275                public boolean accept(final ObjectAction objectAction) {
276                    final Consent visible = objectAction.isVisible(session, target, where);
277                    return visible.isAllowed();
278                }
279            };
280        }
281
282        /**
283         * @deprecated -use {@link com.google.common.base.Predicate equivalent}
284         */
285        @Deprecated
286        public static Filter<ObjectAction> withId(final String actionId) {
287            return new Filter<ObjectAction>(){
288                @Override
289                public boolean accept(ObjectAction objectAction) {
290                    return objectAction.getId().equals(actionId);
291                }
292            };
293        }
294
295        /**
296         * @deprecated -use {@link com.google.common.base.Predicate equivalent}
297         */
298        @Deprecated
299        public static Filter<ObjectAction> withNoValidationRules() {
300            return new Filter<ObjectAction>(){
301                @Override
302                public boolean accept(final ObjectAction objectAction) {
303                    final List<Facet> validatingFacets = objectAction.getFacets(FacetFilters.isA(ValidatingInteractionAdvisor.class));
304                    return validatingFacets.isEmpty();
305                }};
306        }
307
308        /**
309         * @deprecated -use {@link com.google.common.base.Predicate equivalent}
310         */
311        @Deprecated
312        public static Filter<ObjectAction> ofType(final ActionType type) {
313            return new Filter<ObjectAction>(){
314                @Override
315                public boolean accept(ObjectAction oa) {
316                    return oa.getType() == type;
317                }
318            };
319        }
320
321        /**
322         * @deprecated -use {@link com.google.common.base.Predicate equivalent}
323         */
324        @Deprecated
325        public static Filter<ObjectAction> bulk() {
326            return new Filter<ObjectAction>(){
327
328                @Override
329                public boolean accept(ObjectAction oa) {
330                    if( !oa.containsDoOpFacet(BulkFacet.class)) {
331                        return false;
332                    }
333                    if (oa.getParameterCount() != 0) {
334                        return false;
335                    } 
336                    
337                    // currently don't support returning Blobs or Clobs
338                    // (because haven't figured out how to rerender the current page, but also to do a download)
339                    ObjectSpecification returnSpec = oa.getReturnType();
340                    if(returnSpec != null) {
341                        Class<?> returnType = returnSpec.getCorrespondingClass();
342                        if(returnType == Blob.class || returnType == Clob.class) {
343                            return false;
344                        }
345                    }
346                    return true;
347                }};
348        }
349
350        /**
351         * @deprecated -use {@link com.google.common.base.Predicate equivalent}
352         */
353        @Deprecated
354        public static Filter<ObjectAction> notBulkOnly() {
355            return new Filter<ObjectAction>(){
356
357                @Override
358                public boolean accept(ObjectAction t) {
359                    BulkFacet facet = t.getFacet(BulkFacet.class);
360                    return facet == null || facet.value() != Bulk.AppliesTo.BULK_ONLY;
361                }};
362        }
363
364
365        @SuppressWarnings("deprecation")
366        public static Filter<ObjectAction> memberOrderOf(ObjectAssociation association) {
367            final String assocName = association.getName();
368            final String assocId = association.getId();
369            return new Filter<ObjectAction>() {
370        
371                @Override
372                public boolean accept(ObjectAction t) {
373                    final MemberOrderFacet memberOrderFacet = t.getFacet(MemberOrderFacet.class);
374                    if(memberOrderFacet == null) {
375                        return false; 
376                    }
377                    final String memberOrderName = memberOrderFacet.name();
378                    if(Strings.isNullOrEmpty(memberOrderName)) {
379                        return false;
380                    }
381                    return memberOrderName.equalsIgnoreCase(assocName) || memberOrderName.equalsIgnoreCase(assocId);
382                }
383            };
384        }
385    }
386}