001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.isis.core.metamodel.specloader.specimpl; 018 019import java.util.Arrays; 020import java.util.Collections; 021import java.util.List; 022 023import com.google.common.collect.Lists; 024 025import org.apache.isis.applib.Identifier; 026import org.apache.isis.applib.annotation.Bulk; 027import org.apache.isis.applib.annotation.Bulk.InteractionContext.InvokedAs; 028import org.apache.isis.applib.annotation.Where; 029import org.apache.isis.applib.filter.Filter; 030import org.apache.isis.applib.services.bookmark.Bookmark; 031import org.apache.isis.applib.services.command.Command; 032import org.apache.isis.applib.services.command.CommandContext; 033import org.apache.isis.applib.services.command.Command.Executor; 034import org.apache.isis.core.commons.authentication.AuthenticationSession; 035import org.apache.isis.core.commons.lang.ObjectExtensions; 036import org.apache.isis.core.metamodel.adapter.ObjectAdapter; 037import org.apache.isis.core.metamodel.consent.Consent; 038import org.apache.isis.core.metamodel.consent.InteractionInvocationMethod; 039import org.apache.isis.core.metamodel.consent.InteractionResult; 040import org.apache.isis.core.metamodel.deployment.DeploymentCategory; 041import org.apache.isis.core.metamodel.facetapi.Facet; 042import org.apache.isis.core.metamodel.facetapi.FacetHolder; 043import org.apache.isis.core.metamodel.facetapi.FacetHolderImpl; 044import org.apache.isis.core.metamodel.facetapi.FacetUtil; 045import org.apache.isis.core.metamodel.facetapi.MultiTypedFacet; 046import org.apache.isis.core.metamodel.facets.actions.invoke.ActionInvocationFacet; 047import org.apache.isis.core.metamodel.interactions.InteractionUtils; 048import org.apache.isis.core.metamodel.interactions.UsabilityContext; 049import org.apache.isis.core.metamodel.interactions.VisibilityContext; 050import org.apache.isis.core.metamodel.spec.ObjectSpecification; 051import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter; 052import org.apache.isis.core.metamodel.spec.feature.ObjectMemberContext; 053import org.apache.isis.core.progmodel.facets.actions.bulk.BulkFacet; 054import org.apache.isis.core.progmodel.facets.actions.invoke.CommandUtil; 055 056public class ObjectActionContributee extends ObjectActionImpl implements ContributeeMember { 057 058 private final ObjectAdapter serviceAdapter; 059 private final ObjectActionImpl serviceAction; 060 private final int contributeeParam; 061 private final ObjectSpecification contributeeType; 062 063 /** 064 * Hold facets rather than delegate to the contributed action (different types might 065 * use layout metadata to position the contributee in different ways) 066 */ 067 private final FacetHolder facetHolder = new FacetHolderImpl(); 068 069 /** 070 * Lazily initialized by {@link #getParameters()} (so don't use directly!) 071 */ 072 private List<ObjectActionParameterContributee> parameters; 073 074 075 private final Identifier identifier; 076 077 /** 078 * @param contributeeParam - the parameter number which corresponds to the contributee, and so should be suppressed. 079 */ 080 public ObjectActionContributee( 081 final ObjectAdapter serviceAdapter, 082 final ObjectActionImpl serviceAction, 083 final int contributeeParam, 084 final ObjectSpecification contributeeType, 085 final ObjectMemberContext objectMemberContext) { 086 super(serviceAction.getFacetedMethod(), objectMemberContext); 087 088 this.serviceAdapter = serviceAdapter; 089 this.serviceAction = serviceAction; 090 this.contributeeType = contributeeType; 091 this.contributeeParam = contributeeParam; 092 093 // copy over facets from contributed to own. 094 FacetUtil.copyFacets(serviceAction.getFacetedMethod(), facetHolder); 095 096 // calculate the identifier 097 final Identifier contributorIdentifier = serviceAction.getFacetedMethod().getIdentifier(); 098 final String memberName = contributorIdentifier.getMemberName(); 099 List<String> memberParameterNames = contributorIdentifier.getMemberParameterNames(); 100 identifier = Identifier.actionIdentifier(getOnType().getCorrespondingClass().getName(), memberName, memberParameterNames); 101 } 102 103 @Override 104 public ObjectSpecification getOnType() { 105 return contributeeType; 106 } 107 108 public int getParameterCount() { 109 return serviceAction.getParameterCount() - 1; 110 } 111 112 public int getContributeeParam() { 113 return contributeeParam; 114 } 115 116 public synchronized List<ObjectActionParameter> getParameters() { 117 118 if (this.parameters == null) { 119 final List<ObjectActionParameter> serviceParameters = serviceAction.getParameters(); 120 121 final List<ObjectActionParameterContributee> contributeeParameters = Lists.newArrayList(); 122 123 int contributeeParamNum = 0; 124 for (int serviceParamNum = 0; serviceParamNum < serviceParameters.size(); serviceParamNum++ ) { 125 if(serviceParamNum == contributeeParam) { 126 // skip so is omitted from the Contributed action 127 continue; 128 } 129 130 final ObjectActionParameterAbstract serviceParameter = 131 (ObjectActionParameterAbstract) serviceParameters.get(serviceParamNum); 132 final ObjectActionParameterContributee contributedParam; 133 if(serviceParameter instanceof ObjectActionParameterParseable) { 134 contributedParam = new ObjectActionParameterParseableContributee( 135 serviceAdapter, serviceAction, serviceParameter, serviceParamNum, 136 contributeeParamNum, this); 137 } else if(serviceParameter instanceof OneToOneActionParameterImpl) { 138 contributedParam = new OneToOneActionParameterContributee( 139 serviceAdapter, serviceAction, serviceParameter, serviceParamNum, 140 contributeeParamNum, this); 141 } else { 142 throw new RuntimeException("Unknown implementation of ObjectActionParameter; " + serviceParameter.getClass().getName()); 143 } 144 contributeeParameters.add(contributedParam); 145 146 contributeeParamNum++; 147 } 148 this.parameters = contributeeParameters; 149 } 150 return ObjectExtensions.asListT(parameters, ObjectActionParameter.class); 151 } 152 153 154 @Override 155 public Consent isVisible(final AuthenticationSession session, final ObjectAdapter contributee, Where where) { 156 final VisibilityContext<?> ic = serviceAction.createVisibleInteractionContext(session, InteractionInvocationMethod.BY_USER, serviceAdapter, where); 157 ic.putContributee(this.contributeeParam, contributee); 158 return InteractionUtils.isVisibleResult(this, ic).createConsent(); 159 } 160 161 @Override 162 public Consent isUsable(final AuthenticationSession session, final ObjectAdapter contributee, Where where) { 163 final UsabilityContext<?> ic = serviceAction.createUsableInteractionContext(session, InteractionInvocationMethod.BY_USER, serviceAdapter, where); 164 ic.putContributee(this.contributeeParam, contributee); 165 return InteractionUtils.isUsableResult(this, ic).createConsent(); 166 } 167 168 @Override 169 public ObjectAdapter[] getDefaults(final ObjectAdapter target) { 170 final ObjectAdapter[] contributorDefaults = serviceAction.getDefaults(serviceAdapter); 171 return removeElementFromArray(contributorDefaults, contributeeParam, new ObjectAdapter[]{}); 172 } 173 174 @Override 175 public ObjectAdapter[][] getChoices(final ObjectAdapter target) { 176 final ObjectAdapter[][] serviceChoices = serviceAction.getChoices(serviceAdapter); 177 return removeElementFromArray(serviceChoices, contributeeParam, new ObjectAdapter[][]{}); 178 } 179 180 public Consent isProposedArgumentSetValid(final ObjectAdapter contributee, final ObjectAdapter[] proposedArguments) { 181 ObjectAdapter[] serviceArguments = argsPlusContributee(contributee, proposedArguments); 182 return serviceAction.isProposedArgumentSetValid(serviceAdapter, serviceArguments); 183 } 184 185 @Override 186 public ObjectAdapter execute(final ObjectAdapter contributee, final ObjectAdapter[] arguments) { 187 188 // this code also exists in ActionInvocationFacetViaMethod 189 // we need to repeat it here because the target adapter should be the contributee, not the contributing service. 190 final Bulk.InteractionContext bulkInteractionContext = getServicesProvider().lookupService(Bulk.InteractionContext.class); 191 192 final BulkFacet bulkFacet = getFacet(BulkFacet.class); 193 if (bulkFacet != null && 194 bulkInteractionContext != null && 195 bulkInteractionContext.getInvokedAs() == null) { 196 197 bulkInteractionContext.setInvokedAs(InvokedAs.REGULAR); 198 bulkInteractionContext.setDomainObjects(Collections.singletonList(contributee.getObject())); 199 } 200 201 final CommandContext commandContext = getServicesProvider().lookupService(CommandContext.class); 202 final Command command = commandContext != null ? commandContext.getCommand() : null; 203 204 if(command != null && command.getExecutor() == Executor.USER) { 205 206 command.setTargetClass(CommandUtil.targetClassNameFor(contributee)); 207 command.setTargetAction(CommandUtil.targetActionNameFor(this)); 208 command.setArguments(CommandUtil.argDescriptionFor(this, arguments)); 209 210 final Bookmark targetBookmark = CommandUtil.bookmarkFor(contributee); 211 command.setTarget(targetBookmark); 212 } 213 214 return serviceAction.execute(serviceAdapter, argsPlusContributee(contributee, arguments)); 215 } 216 217 private ObjectAdapter[] argsPlusContributee(final ObjectAdapter contributee, final ObjectAdapter[] arguments) { 218 return addElementToArray(arguments, contributeeParam, contributee, new ObjectAdapter[]{}); 219 } 220 221 // ////////////////////////////////////// 222 // FacetHolder 223 // ////////////////////////////////////// 224 225 @Override 226 public Class<? extends Facet>[] getFacetTypes() { 227 return facetHolder.getFacetTypes(); 228 } 229 230 @Override 231 public <T extends Facet> T getFacet(Class<T> cls) { 232 return facetHolder.getFacet(cls); 233 } 234 235 @Override 236 public boolean containsFacet(Class<? extends Facet> facetType) { 237 return facetHolder.containsFacet(facetType); 238 } 239 240 @Override 241 public boolean containsDoOpFacet(java.lang.Class<? extends Facet> facetType) { 242 return facetHolder.containsDoOpFacet(facetType); 243 } 244 245 @Override 246 public List<Facet> getFacets(Filter<Facet> filter) { 247 return facetHolder.getFacets(filter); 248 } 249 250 @Override 251 public void addFacet(Facet facet) { 252 facetHolder.addFacet(facet); 253 } 254 255 @Override 256 public void addFacet(MultiTypedFacet facet) { 257 facetHolder.addFacet(facet); 258 } 259 260 @Override 261 public void removeFacet(Facet facet) { 262 facetHolder.removeFacet(facet); 263 } 264 265 @Override 266 public void removeFacet(Class<? extends Facet> facetType) { 267 facetHolder.removeFacet(facetType); 268 } 269 270 271 // ////////////////////////////////////// 272 273 /* (non-Javadoc) 274 * @see org.apache.isis.core.metamodel.specloader.specimpl.ObjectMemberAbstract#getIdentifier() 275 */ 276 @Override 277 public Identifier getIdentifier() { 278 return identifier; 279 } 280 281 // ////////////////////////////////////// 282 283 static <T> T[] addElementToArray(T[] array, final int n, final T element, final T[] type) { 284 List<T> list = Lists.newArrayList(Arrays.asList(array)); 285 list.add(n, element); 286 return list.toArray(type); 287 } 288 289 static <T> T[] removeElementFromArray(T[] array, int n, T[] t) { 290 List<T> list = Lists.newArrayList(Arrays.asList(array)); 291 list.remove(n); 292 return list.toArray(t); 293 } 294 295 296}